Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Wskazówka
Jeśli czytałeś ten temat wcześniej i wracasz do niego z zamiarem wykonania konkretnego zadania, możesz przejść do sekcji Znajdź zawartość na podstawie zadania, które wykonujesz w tym wątku.
Ten temat kompleksowo zawiera wykaz szczegółów technicznych związanych z przenoszeniem kodu źródłowego w projekcie języka C#
Aby zapoznać się z analizą przypadku przenoszenia jednej z przykładów aplikacji platformy uniwersalnej systemu Windows (UWP), zobacz temat towarzyszący Przenoszenie przykładu Schowka do języka C++/WinRT zjęzyka C#. Możesz zdobyć praktykę i doświadczenie w przenoszeniu, postępując zgodnie z tym przewodnikiem i przenosząc przykład samodzielnie w miarę postępów.
Jak przygotować się i czego się spodziewać
Analiza przypadku Przenoszenie przykładu Schowka do języka C++/WinRT z języka C# ilustruje przykłady rodzajów decyzji projektowych oprogramowania, które należy podjąć podczas przenoszenia projektu do języka C++/WinRT. Dlatego warto przygotować się do przenoszenia, zdobywając solidne zrozumienie sposobu działania istniejącego kodu. W ten sposób uzyskasz dobry przegląd funkcjonalności aplikacji i struktury kodu, a następnie decyzje, które podejmujesz, zawsze będą podejmowane do przodu i w odpowiednim kierunku.
Jeśli chodzi o to, jakich zmian w przenoszeniu można się spodziewać, można je zgrupować w cztery kategorie.
-
Przenieś projekcję języka. Środowisko uruchomieniowe systemu Windows (WinRT) jest przewidywane do różnych języków programowania. Każda z tych projekcji języka została zaprojektowana tak, aby czuła się idiomatyczna dla danego języka programowania. W przypadku języka C# niektóre typy środowiska uruchomieniowego systemu Windows są przewidywane jako typy platformy .NET. Na przykład przetłumaczysz element System.Collections.Generic.IReadOnlyList<T> z powrotem do elementu Windows.Foundation.Collections.IVectorView<T>. Ponadto w języku C# niektóre operacje środowiska uruchomieniowego systemu Windows są przewidywane jako wygodne funkcje języka C#. Na przykład w języku C# używa się składni operatora
+=do zarejestrowania delegata obsługującego zdarzenie. Dlatego będziesz tłumaczyć funkcje języka, takie jak ta, do podstawowej operacji, która jest wykonywana (rejestracja zdarzeń, w tym przykładzie). -
Składnia języka portów. Wiele z tych zmian to proste przekształcenia mechaniczne, zastępując jeden symbol dla innego. Na przykład zmiana kropki (
.) na dwukropek (::). -
Procedura języka portów. Niektóre z nich mogą być prostymi, powtarzalnymi zmianami (takimi jak
myObject.MyPropertymyObject.MyProperty()). Inne wymagają głębszych zmian (na przykład przeniesienie procedury, która wykorzystuje System.Text.StringBuilder na taką, która wykorzystuje std::wostringstream). -
zadania związane z przenoszeniem specyficzne dla języka C++/WinRT. Niektóre szczegóły środowiska uruchomieniowego systemu Windows są obsługiwane niejawnie przez język C#, w tle. Szczegóły te są realizowane jawnie w C++/WinRT. Przykładem jest użycie
.idlpliku do zdefiniowania klas środowiska uruchomieniowego.
Po następującym indeksie opartym na zadaniach pozostałe sekcje w tym temacie są ustrukturyzowane zgodnie z taksonomią powyżej.
Znajdowanie zawartości na podstawie wykonywanego zadania
| Zadanie | Zawartość |
|---|---|
| Tworzenie składnika środowiska uruchomieniowego systemu Windows (WRC) | Niektóre funkcje można osiągnąć (lub niektóre wywoływane interfejsy API) tylko w języku C++. Możesz uwzględnić te funkcje w języku C++/WinRT WRC, a następnie korzystać z WRC z (na przykład) aplikacji języka C#. Zobacz składniki środowiska uruchomieniowego systemu Windows w języku C++/WinRT i Jeśli tworzysz klasę środowiska uruchomieniowego w składniku środowiska uruchomieniowego systemu Windows. |
| Przenieś metodę asynchroniczną | Dobrym pomysłem jest, aby pierwszy wiersz metody asynchronicznej w klasie środowiska uruchomieniowego C++/WinRT był auto lifetime = get_strong(); (zobacz Bezpieczne uzyskiwanie dostępu do wskaźnika this w korutynie składowej klasy).Przenoszenie z Task, zobacz akcję asynchroniczną.Przenoszenie z Task<T>— zobacz operację asynchroniczną.Przenoszenie z async void, metoda Fire-and-forget, patrz . |
| Portowanie klasy | Najpierw określ, czy klasa musi być klasą środowiska uruchomieniowego, czy też może być zwykłą klasą. Aby ułatwić podjęcie decyzji, zobacz początek interfejsów API tworzenia przy użyciu języka C++/WinRT. Następnie zapoznaj się z poniższymi trzema kolejnymi wierszami. |
| Przenoszenie klasy środowiska uruchomieniowego | Klasa, która udostępnia funkcje poza aplikacją języka C++ lub klasę używaną w powiązaniu danych XAML. Zobacz , jeśli tworzysz klasę uruchomieniową w składniku Windows Runtime, lub , jeśli klasa ma być referencją w interfejsie użytkownika XAML. Te linki opisują to bardziej szczegółowo, ale klasa środowiska uruchomieniowego musi być zadeklarowana w języku IDL. Jeśli projekt zawiera już plik IDL (na przykład Project.idl), zalecamy zadeklarowanie dowolnej nowej klasy środowiska uruchomieniowego w tym pliku. W języku IDL zadeklaruj wszystkie metody i składowe danych, które będą używane poza aplikacją lub będą używane w języku XAML. Po zaktualizowaniu pliku IDL, przebuduj projekt i sprawdź wygenerowane pliki szkieletowe (.h i .cpp) w folderze Generated Files projektu (w Eksploratorze rozwiązań , z wybranym węzłem projektu, upewnij się, że opcja Pokaż wszystkie pliki jest włączona). Porównaj pliki wycinkowe z plikami już w projekcie, dodając pliki lub dodając/aktualizując podpisy funkcji w razie potrzeby. Składnia pliku wycinkowego jest zawsze poprawna, dlatego zalecamy użycie jej w celu zminimalizowania błędów kompilacji. Gdy szablony w projekcie będą zgodne z elementami w plikach szablonów, możesz je wdrożyć, przenosząc kod języka C#. |
| Przenoszenie zwykłej klasy | Zobacz , jeśli nie tworzysz klasy środowiska uruchomieniowego. |
| Autor IDL |
Wprowadzenie do języka Microsoft Interface Definition Language 3.0 Jeśli tworzysz klasę uruchomieniową do użycia w UI XAML Konsumowanie obiektów ze znaczników XAML Definiowanie klas środowiska uruchomieniowego w języku IDL |
| Przenoszenie kolekcji | Kolekcje z C++/WinRT Umożliwienie dostępu do źródła danych w znacznikach XAML kontener asocjacyjny Dostęp do elementu wektora |
| Portowanie zdarzenia | delegowanie programu obsługi zdarzeń jako składowej klasy Odwołaj delegata obsługi zdarzeń |
| Portowanie metody | W języku C#: private async void SampleButton_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) { ... }Do pliku .h C++/WinRT: fire_and_forget SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&);Do pliku .cpp C++/WinRT: fire_and_forget OcrFileImage::SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&) {...} |
| Łańcuchy portów | Obsługa stringów w C++/WinRT ToString tworzenie ciągów Boksowanie i rozpakowywanie ciągu znaków |
| Konwersja typów (rzutowanie typów) | C#: o.ToString()C++/WinRT: to_hstring(static_cast<int>(o))Patrz również ToString. C#: (Value)oC++/WinRT: unbox_value<Value>(o)Rzuca wyjątek, jeśli rozpakowywanie się nie powiedzie. Zobacz również Boxing i rozpakowywanie. C#: o as Value? ?? fallbackC++/WinRT: unbox_value_or<Value>(o, fallback)Zwraca wartość domyślną, jeśli rozpakowanie się nie powiedzie. Zobacz również Boxing i rozpakowywanie. C#: (Class)oC++/WinRT: o.as<Class>()Zgłasza błąd w przypadku niepowodzenia konwersji. C#: o as ClassC++/WinRT: o.try_as<Class>()Zwraca wartość null, jeśli konwersja nie powiedzie się. |
Zmiany, które obejmują projekcję języka
| Kategoria | C# | C++/WinRT | Zobacz także |
|---|---|---|---|
| Nietypowany obiekt |
objectlub System.Object |
Windows::Foundation::IInspectable | Przenoszenie metody EnableClipboardContentChangedNotifications |
| Przestrzenie nazw projekcji | using System; |
using namespace Windows::Foundation; |
|
using System.Collections.Generic; |
using namespace Windows::Foundation::Collections; |
||
| Rozmiar kolekcji | collection.Count |
collection.Size() |
Przenoszenie metody BuildClipboardFormatsOutputString |
| Typowy typ kolekcji | IList<T> i Dodaj aby dodać element. | IVector<T>i Append w celu dodania elementu. Jeśli używasz std::vector w dowolnym miejscu, użyj push_back, aby dodać element. | |
| Typ kolekcji tylko do odczytu | IReadOnlyList<T> | IVectorView<T> | Przenoszenie metody BuildClipboardFormatsOutputString |
| delegat programu obsługi zdarzeń jako element klasy | myObject.EventName += Handler; |
token = myObject.EventName({ get_weak(), &Class::Handler }); |
Przenoszenie metody EnableClipboardContentChangedNotifications |
| odwołać delegata procedury obsługi zdarzeń | myObject.EventName -= Handler; |
myObject.EventName(token); |
Przenoszenie metody EnableClipboardContentChangedNotifications |
| kontener asocjacyjny | ISłownik<K, V> | IMap<K, V> | |
| dostęp do elementu członkowskiego Vector | x = v[i];v[i] = x; |
x = v.GetAt(i);v.SetAt(i, x); |
Rejestrowanie/odwoływanie obsługi zdarzeń
W języku C++/WinRT masz kilka opcji składniowych umożliwiających rejestrowanie/odwoływanie delegata procedury obsługi zdarzeń zgodnie z opisem w Obsługa zdarzeń przy użyciu delegatów w języku C++/WinRT. Zobacz również Przenoszenie metody EnableClipboardContentChangedNotifications metody.
Czasami, gdy adresat zdarzenia (obiekt obsługujący zdarzenie) ma zostać zniszczony, należy odwołać procedurę obsługi zdarzeń, aby źródło zdarzeń (obiekt wywołujący zdarzenie) nie wywołuje zniszczonego obiektu. Sprawdź Odwołanie zarejestrowanego pełnomocnika. W takich przypadkach utwórz zmienną event_token składową dla programów obsługi zdarzeń. Aby zapoznać się z przykładem, zobacz Przenoszenie metody EnableClipboardContentChangedNotifications.
Można również zarejestrować procedurę obsługi zdarzeń w adiustacji XAML.
<Button x:Name="OpenButton" Click="OpenButton_Click" />
W języku C# metoda OpenButton_Click może być prywatna, a kod XAML nadal będzie mógł połączyć się z ButtonBase.Click zdarzeniem wywoływanym przez OpenButton.
W C++/WinRT metoda OpenButton_Click musi być publiczna w typie implementacji, jeśli chcesz ją zarejestrować w znacznikach XAML. Jeśli zarejestrujesz program obsługi zdarzeń tylko w kodzie imperatywnym, program obsługi zdarzeń nie musi być publiczny.
namespace winrt::MyProject::implementation
{
struct MyPage : MyPageT<MyPage>
{
void OpenButton_Click(
winrt::Windows:Foundation::IInspectable const& sender,
winrt::Windows::UI::Xaml::RoutedEventArgs const& args);
}
};
Alternatywnie możesz ustawić stronę rejestracji XAML jako przyjaciela typu implementacji i OpenButton_Click prywatną.
namespace winrt::MyProject::implementation
{
struct MyPage : MyPageT<MyPage>
{
private:
friend MyPageT;
void OpenButton_Click(
winrt::Windows:Foundation::IInspectable const& sender,
winrt::Windows::UI::Xaml::RoutedEventArgs const& args);
}
};
Ostatnim scenariuszem jest miejsce, w którym projekt języka C#, który przenosi wiąże z programem obsługi zdarzeń z znaczników (aby uzyskać więcej informacji na temat tego scenariusza, zobacz Functions w pliku x:Bind).
<Button x:Name="OpenButton" Click="{x:Bind OpenButton_Click}" />
Możesz po prostu zmienić ten znacznik na bardziej prosty Click="OpenButton_Click". Jeśli wolisz, możesz zachować ten znacznik tak, jak to jest. Wszystko, co musisz zrobić, aby go obsługiwać, to zadeklarować program obsługi zdarzeń w języku IDL.
void OpenButton_Click(Object sender, Windows.UI.Xaml.RoutedEventArgs e);
Uwaga / Notatka
Zadeklaruj funkcję jako void, nawet jeśli zaimplementujesz ją jako "Fire and forget".
Zmiany, które obejmują składnię języka
| Kategoria | C# | C++/WinRT | Zobacz także |
|---|---|---|---|
| Modyfikatory dostępu | public \<member\> |
public:\<member\> |
Przenoszenie metody Button_Click |
| Uzyskiwanie dostępu do elementu członkowskiego danych | this.variable |
this->variable |
|
| akcja asynchroniczna | async Task ... |
IAsyncAction ... |
interfejs IAsyncAction, współbieżność i operacje asynchroniczne za pomocą języka C++/WinRT |
| operacja asynchroniczna | async Task<T> ... |
IAsyncOperation<T> ... |
interfejs IAsyncOperation, współbieżność i operacje asynchroniczne w języku C++/WinRT |
| metoda "fire-and-forget" (oznacza metodę asynchroniczną) | async void ... |
winrt::fire_and_forget ... |
Portowanie metody CopyButton_Click, Fire and forget |
| Uzyskiwanie dostępu do wyliczonej stałej | E.Value |
E::Value |
przenoszenie metody DisplayChangedFormats |
| Wspólne oczekiwanie | await ... |
co_await ... |
Przenoszenie metody CopyButton_Click |
| Kolekcja projektowanych typów jako prywatne pole | private List<MyRuntimeClass> myRuntimeClasses = new List<MyRuntimeClass>(); |
std::vector<MyNamespace::MyRuntimeClass>m_myRuntimeClasses; |
|
| Konstrukcja identyfikatora GUID | private static readonly Guid myGuid = new Guid("C380465D-2271-428C-9B83-ECEA3B4A85C1"); |
winrt::guid myGuid{ 0xC380465D, 0x2271, 0x428C, { 0x9B, 0x83, 0xEC, 0xEA, 0x3B, 0x4A, 0x85, 0xC1} }; |
|
| Separator przestrzeni nazw | A.B.T |
A::B::T |
|
| Zero | null |
nullptr |
Przeniesienie metody UpdateStatus |
| Uzyskiwanie obiektu typu | typeof(MyType) |
winrt::xaml_typename<MyType>() |
Przenoszenie właściwości Scenarios |
| Deklaracja parametru dla metody | MyType |
MyType const& |
przekazywanie parametrów |
| Deklaracja parametru dla metody asynchronicznej | MyType |
MyType |
przekazywanie parametrów |
| Wywoływanie metody statycznej | T.Method() |
T::Method() |
|
| Ciągi |
string, lub System.String |
winrt::hstring | Obsługa stringów w C++/WinRT |
| Literał ciągu | "a string literal" |
L"a string literal" |
Przenoszenie konstruktora Currenti FEATURE_NAME |
| Typ wywnioskowany (lub wyprowadzony) | var |
auto |
Przenoszenie metody BuildClipboardFormatsOutputString |
| Korzystanie z dyrektywy | using A.B.C; |
using namespace A::B::C; |
Przenoszenie konstruktora Currenti FEATURE_NAME |
| Literał dosłownego łańcucha znaków/surowego łańcucha znaków | @"verbatim string literal" |
LR"(raw string literal)" |
Przenoszenie DisplayToast metody |
Uwaga / Notatka
Jeśli plik nagłówka nie zawiera dyrektywy using namespace dla danej przestrzeni nazw, musisz w pełni zakwalifikować wszystkie nazwy typów dla tej przestrzeni nazw lub przynajmniej aby były wystarczająco kwalifikowane, aby kompilator je znalazł. Aby zapoznać się z przykładem, zobacz Przenoszenie metody DisplayToast.
Przenoszenie klas i elementów
Musisz zdecydować, czy dla każdego typu C# należy go przenieść do typu Windows Runtime, czy też do zwykłej klasy/struktury/wyliczenia C++. Aby uzyskać więcej informacji i szczegółowe przykłady ilustrujące sposób podejmowania tych decyzji, zobacz Przeniesienie przykładu Schowka z języka C# do C++/WinRT.
Właściwość języka C# zwykle staje się funkcją dostępu, funkcją mutatora oraz polem danych. Aby uzyskać więcej informacji i przykład, zobacz Przenoszenie właściwości IsClipboardContentChangedEnabled.
W przypadku pól niestatycznych, zrób z nich składowe danych typu implementacji .
Pole statyczne języka C# staje się statycznym akcesorem C++/WinRT i/lub funkcją mutatora. Aby uzyskać więcej informacji i przykład, zobacz Przenoszenie konstruktora, Obecnyi FEATURE_NAME.
W przypadku funkcji składowych należy ponownie zdecydować dla każdej z osobna, czy powinna należeć do IDL, czy jest funkcją publiczną lub prywatną w typie implementacji. Aby uzyskać więcej informacji i przykłady sposobu podejmowania decyzji, zobacz IDL dla MainPage typu.
Przenoszenie znaczników XAML i plików zasobów
W przypadku Przenoszenie przykładu Schowka do języka C++/WinRT zjęzyka C# mogliśmy użyć tego samego znaczników XAML (w tym zasobów) i plików elementów zawartości w języku C# i projekcie C++/WinRT. W niektórych przypadkach konieczne będzie wprowadzenie zmian w znacznikach. Zapoznaj się z , skopiuj XAML oraz style niezbędne do zakończenia migracji MainPage.
Zmiany obejmujące procedury w języku
| Kategoria | C# | C++/WinRT | Zobacz także |
|---|---|---|---|
| Zarządzanie cyklem życia w metodzie asynchronicznej | N/A |
auto lifetime{ get_strong() }; lubauto lifetime = get_strong(); |
Przenoszenie metody CopyButton_Click |
| Utylizacja | using (var t = v) |
auto t{ v };t.Close(); // or let wrapper destructor do the work |
|
| Konstruowanie obiektu | new MyType(args) |
MyType{ args } lubMyType(args) |
Przenoszenie właściwości Scenarios |
| Tworzenie niezainicjowanej referencji | MyType myObject; |
MyType myObject{ nullptr }; lubMyType myObject = nullptr; |
Przenoszenie konstruktora Currenti FEATURE_NAME |
| Budowa obiektu jako zmiennej z args | var myObject = new MyType(args); |
auto myObject{ MyType{ args } }; lub auto myObject{ MyType(args) }; lub auto myObject = MyType{ args }; lub auto myObject = MyType(args); lub MyType myObject{ args }; lub MyType myObject(args); |
Portowanie metody Footer_Click |
| Konstruowanie obiektu w zmiennej bez args | var myObject = new T(); |
MyType myObject; |
Przenoszenie metody BuildClipboardFormatsOutputString |
| Skrót inicjowania obiektu | var p = new FileOpenPicker{ViewMode = PickerViewMode.List}; |
FileOpenPicker p;p.ViewMode(PickerViewMode::List); |
|
| Operacje wektorowe zbiorcze | var p = new FileOpenPicker{FileTypeFilter = { ".png", ".jpg", ".gif" }}; |
FileOpenPicker p;p.FileTypeFilter().ReplaceAll({ L".png", L".jpg", L".gif" }); |
Przenoszenie metody CopyButton_Click |
| Iterowanie w kolekcji | foreach (var v in c) |
for (auto&& v : c) |
Przenoszenie metody BuildClipboardFormatsOutputString |
| Przechwyć wyjątek | catch (Exception ex) |
catch (winrt::hresult_error const& ex) |
Przenoszenie metody PasteButton_Click |
| Szczegóły wyjątku | ex.Message |
ex.message() |
Przenoszenie metody PasteButton_Click |
| Pobierz wartość właściwości | myObject.MyProperty |
myObject.MyProperty() |
|
| Ustaw wartość właściwości | myObject.MyProperty = value; |
myObject.MyProperty(value); |
|
| Zwiększanie wartości właściwości | myObject.MyProperty += v; |
myObject.MyProperty(thing.Property() + v);W przypadku ciągów użyj obiektu Builder |
|
| ToString() | myObject.ToString() |
winrt::to_hstring(myObject) |
ToString() |
| Ciąg języka do ciągu środowiska uruchomieniowego systemu Windows | N/A | winrt::hstring{ s } |
|
| Tworzenie ciągów | StringBuilder builder;builder.Append(...); |
std::wostringstream builder;builder << ...; |
tworzenie ciągów |
| Interpolacja ciągów | $"{i++}) {s.Title}" |
winrt::to_hstringi/lub winrt::hstring::operator+ | Przenoszenie metody OnNavigatedTo |
| Pusty ciąg do porównania | System.String.Empty | winrt::hstring::empty | Przeniesienie metody UpdateStatus |
| Tworzenie pustego ciągu | var myEmptyString = String.Empty; |
winrt::hstring myEmptyString{ L"" }; |
|
| Operacje słownika | map[k] = v; // replaces any existingv = map[k]; // throws if not presentmap.ContainsKey(k) |
map.Insert(k, v); // replaces any existingv = map.Lookup(k); // throws if not presentmap.HasKey(k) |
|
| Konwersja typu (zgłaszanie błędu) | (MyType)v |
v.as<MyType>() |
Portowanie metody Footer_Click |
| Konwersja typu (wartość null w przypadku niepowodzenia) | v as MyType |
v.try_as<MyType>() |
Przenoszenie metody PasteButton_Click |
| Elementy XAML z właściwościami x:Name | MyNamedElement |
MyNamedElement() |
Przenoszenie konstruktora Currenti FEATURE_NAME |
| Przełącz się do wątku interfejsu użytkownika | CoreDispatcher.RunAsync | CoreDispatcher.RunAsynclub winrt::resume_foreground | Przenoszenie metody NotifyUseri Przenoszenie metody HistoryAndRoaming |
| Konstrukcja elementu interfejsu użytkownika w kodzie imperatywnego na stronie XAML | Zobacz konstruowanie elementów interfejsu użytkownika | Zobacz konstruowanie elementów interfejsu użytkownika |
Poniższe sekcje zawierają bardziej szczegółowe informacje dotyczące niektórych elementów w tabeli.
Konstrukcja elementu interfejsu użytkownika
Te przykłady kodu pokazują konstrukcję elementu interfejsu użytkownika w kodzie imperatywnej strony XAML.
var myTextBlock = new TextBlock()
{
Text = "Text",
Style = (Windows.UI.Xaml.Style)this.Resources["MyTextBlockStyle"]
};
TextBlock myTextBlock;
myTextBlock.Text(L"Text");
myTextBlock.Style(
winrt::unbox_value<Windows::UI::Xaml::Style>(
Resources().Lookup(
winrt::box_value(L"MyTextBlockStyle")
)
)
);
ToString()
Typy języka C# udostępniają metodę Object.ToString .
int i = 2;
var s = i.ToString(); // s is a System.String with value "2".
Język C++/WinRT nie zapewnia bezpośrednio tej funkcji, ale możesz zwrócić się do alternatyw.
int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".
Język C++/WinRT obsługuje również winrt::to_hstring dla ograniczonej liczby typów. Należy dodać przeciążenia dla dodatkowych typów, które chcesz konwertować na ciągi znaków.
| Język | Konwertuj int na ciąg znaków | Konwertuj wyliczenie na ciąg |
|---|---|---|
| C# | string result = "hello, " + intValue.ToString();string result = $"hello, {intValue}"; |
string result = "status: " + status.ToString();string result = $"status: {status}"; |
| C++/WinRT | hstring result = L"hello, " + to_hstring(intValue); |
// must define overload (see below)hstring result = L"status: " + to_hstring(status); |
W przypadku zamiany wyliczenia na ciąg znaków należy podać implementację winrt::to_hstring.
namespace winrt
{
hstring to_hstring(StatusEnum status)
{
switch (status)
{
case StatusEnum::Success: return L"Success";
case StatusEnum::AccessDenied: return L"AccessDenied";
case StatusEnum::DisabledByPolicy: return L"DisabledByPolicy";
default: return to_hstring(static_cast<int>(status));
}
}
}
Te stringifikacje są często wykorzystywane niejawnie przez powiązanie danych.
<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>
Te powiązania będą wykonywać winrt::to_hstring właściwości powiązanej. W przypadku drugiego przykładu (StatusEnum), musisz dostarczyć własną wersję przeciążenia funkcji winrt::to_hstring, w przeciwnym razie zostanie wyświetlony błąd kompilatora.
Zobacz również Przenoszenie metody Footer_Click.
Tworzenie ciągów
W przypadku kompilowania ciągów język C# ma wbudowany typ StringBuilder .
| Kategoria | C# | C++/WinRT |
|---|---|---|
| Tworzenie ciągów | StringBuilder builder;builder.Append(...); |
std::wostringstream builder;builder << ...; |
| Dołącz ciąg Windows Runtime z zachowaniem wartości null | builder.Append(s); |
builder << std::wstring_view{ s }; |
| Dodawanie nowego wiersza | builder.Append(Environment.NewLine); |
builder << std::endl; |
| Uzyskiwanie dostępu do wyniku | s = builder.ToString(); |
ws = builder.str(); |
Zobacz również Przenoszenie metody BuildClipboardFormatsOutputStringi Przenoszenie metody DisplayChangedFormats.
Uruchamianie kodu w głównym wątku interfejsu użytkownika
Został zaczerpnięty z przykładu skanera kodów kreskowych .
Jeśli chcesz pracować nad głównym wątkiem interfejsu użytkownika w projekcie języka C#, zazwyczaj używasz metody CoreDispatcher.RunAsync , takiej jak ta.
private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
// Do work on the main UI thread here.
});
}
Znacznie łatwiej jest wyrazić to w języku C++/WinRT. Zwróć uwagę, że przyjmujemy parametry według wartości przy założeniu, że chcemy uzyskać do nich dostęp po pierwszym punkcie zawieszenia ( co_awaitw tym przypadku ). Aby uzyskać więcej informacji, zobacz przekazywanie parametrów.
winrt::fire_and_forget Watcher_Added(DeviceWatcher sender, winrt::DeviceInformation args)
{
co_await Dispatcher();
// Do work on the main UI thread here.
}
Jeśli musisz wykonać pracę z priorytetem innym niż domyślna, zobacz funkcję winrt::resume_foreground, która ma przeciążenie, które ma priorytet. Aby zobaczyć przykłady kodu pokazujące, jak oczekiwać na wywołanie winrt::resume_foreground, zobacz Programowanie z koligacją wątków.
Zadania związane z przenoszeniem specyficzne dla C++/WinRT
Definiowanie klas środowiska uruchomieniowego w języku IDL
Zobacz
Uwzględnij potrzebne pliki nagłówkowe przestrzeni nazw systemu Windows C++/WinRT
W języku C++/WinRT zawsze, gdy chcesz użyć typu z przestrzeni nazw systemu Windows, musisz dołączyć odpowiedni plik nagłówka przestrzeni nazw systemu Windows C++/WinRT. Aby zapoznać się z przykładem, zobacz Przenoszenie metody NotifyUser.
Opakowywanie i rozpakowywanie
Język C# automatycznie opakowuje typy wartości w obiekty. Język C++/WinRT wymaga jawnego wywołania funkcji winrt::box_value. Oba języki wymagają jawnego rozpakowania (unboxingu). Zobacz boksowanie i odboksowanie w C++/WinRT.
W poniższych tabelach użyjemy tych definicji.
| C# | C++/WinRT |
|---|---|
int i; |
int i; |
string s; |
winrt::hstring s; |
object o; |
IInspectable o; |
| Operacja | C# | C++/WinRT |
|---|---|---|
| Boks | o = 1;o = "string"; |
o = box_value(1);o = box_value(L"string"); |
| Rozpakowywanie | i = (int)o;s = (string)o; |
i = unbox_value<int>(o);s = unbox_value<winrt::hstring>(o); |
C++/CX i C# zgłaszają wyjątki, jeśli spróbujesz rozpakować wskaźnik null do typu wartości. C++/WinRT uważa to za błąd programowania i ulega awarii. W języku C++/WinRT użyj funkcji winrt::unbox_value_or , jeśli chcesz obsłużyć przypadek, w którym obiekt nie jest typem, który był uważany za.
| Scenariusz | C# | C++/WinRT |
|---|---|---|
| Rozpakuj znaną liczbę całkowitą | i = (int)o; |
i = unbox_value<int>(o); |
| Jeśli o jest null | System.NullReferenceException |
Wypadek |
| Jeśli o nie jest opakowanym intem | System.InvalidCastException |
Wypadek |
| Rozpakuj int, użyj wartości domyślnej, jeśli jest null; zakończ działanie w przypadku innych wartości | i = o != null ? (int)o : fallback; |
i = o ? unbox_value<int>(o) : fallback; |
| Rozpakuj "int", jeśli to możliwe; użyj alternatywy dla innych przypadków. | i = as int? ?? fallback; |
i = unbox_value_or<int>(o, fallback); |
Aby zapoznać się z przykładem, zobacz Przenoszenie metody OnNavigatedToi Przenoszenie metody Footer_Click.
Boxing i unboxing ciągu znaków
Ciąg jest w jakiś sposób typem wartości i w inny sposób typem odwołania. Języki C# i C++/WinRT traktują ciągi inaczej.
Typ ABI HSTRING jest wskaźnikiem do ciągu znaków zarządzanego referencyjnie. Ale nie pochodzi z IInspectable, więc nie jest to technicznie obiekt . Ponadto, null HSTRING reprezentuje pusty ciąg. Boxowanie elementów, które nie pochodzą z IInspectable, odbywa się poprzez opakowywanie ich wewnątrz IReference<T>, a środowisko wykonawcze Windows Runtime zapewnia standardową implementację w postaci obiektu PropertyValue (typy niestandardowe są zgłaszane jako PropertyType::OtherType).
Język C# reprezentuje ciąg środowiska uruchomieniowego systemu Windows jako typ odwołania; podczas gdy C++/WinRT projektuje ciąg jako typ wartości. Oznacza to, że opakowany ciąg znaków null może mieć różne reprezentacje w zależności od tego, jak do tego doszło.
| Zachowanie | C# | C++/WinRT |
|---|---|---|
| Deklaracje | object o;string s; |
IInspectable o;hstring s; |
| Kategoria typu ciągu | Typ odwołania | Typ wartości |
| null HSTRING projekty jako | "" |
hstring{} |
Czy null i "" są identyczne? |
Nie. | Tak |
| Ważność wartości null | s = null;s.Length zgłasza wyjątek NullReferenceException |
s = hstring{};s.size() == 0 (prawidłowe) |
| W przypadku przypisania ciągu null do obiektu | o = (string)null;o == null |
o = box_value(hstring{});o != nullptr |
Jeśli przypiszesz "" do obiektu |
o = "";o != null |
o = box_value(hstring{L""});o != nullptr |
Podstawy boksowania i rozboksowywania.
| Operacja | C# | C++/WinRT |
|---|---|---|
| Osadzanie ciągu w ramce | o = s;Pusty ciąg staje się obiektem niebędącym wartością null. |
o = box_value(s);Pusty ciąg staje się obiektem niebędącym wartością null. |
| Rozpakuj znany ciąg | s = (string)o;Obiekt o wartości null staje się ciągiem null. InvalidCastException, jeśli nie jest ciągiem. |
s = unbox_value<hstring>(o);Obiekt o wartości null ulega awarii. Zawieszenie, jeśli nie jest ciągiem. |
| Rozpakuj możliwy ciąg | s = o as string;Obiekt o wartości null lub nie będący ciągiem staje się ciągiem null. LUB s = o as string ?? fallback;Wartość null lub wartość niebędąca ciągiem staje się wartością domyślną. Zachowany pusty ciąg. |
s = unbox_value_or<hstring>(o, fallback);Wartość null lub wartość niebędąca ciągiem staje się wartością domyślną. Zachowany pusty ciąg. |
Udostępnianie klasy rozszerzeniu znaczników {Binding}
Jeśli zamierzasz użyć rozszerzenia znaczników {Binding} do wprowadzenia powiązań danych z Twoim typem danych, zobacz Obiekt powiązania zadeklarowany przy użyciu {Binding}.
Korzystanie z obiektów z znaczników XAML
W projekcie języka C# można używać prywatnych elementów członkowskich i nazwanych elementów z znaczników XAML. Jednak w języku C++/WinRT wszystkie elementy używane przy użyciu rozszerzenia znaczników XAML {x:Bind} muszą być dostępne publicznie w IDL.
Ponadto wiązanie z wartością typu Boolean wyświetla true lub false w języku C#, ale pokazuje Windows.Foundation.IReference`1<Boolean> w języku C++/WinRT.
Aby uzyskać więcej informacji oraz przykłady kodu, zobacz Korzystanie z obiektów z markupu.
Udostępnianie źródła danych dla znaczników XAML
W C++/WinRT, począwszy od wersji 2.0.190530.8, winrt::single_threaded_observable_vector tworzy obserwowalny wektor, który obsługuje zarówno IObservableVector<T>, jak i IObservableVector<IInspectable>. Aby zapoznać się z przykładem, zobacz Przenoszenie właściwości Scenarios.
Możesz utworzyć plik Midl (.idl) w następujący sposób (zobacz również podział klas środowiska uruchomieniowego na pliki Midl (.idl)).
namespace Bookstore
{
runtimeclass BookSku { ... }
runtimeclass BookstoreViewModel
{
Windows.Foundation.Collections.IObservableVector<BookSku> BookSkus{ get; };
}
runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
{
MainPage();
BookstoreViewModel MainViewModel{ get; };
}
}
I zaimplementuj w ten sposób.
// BookstoreViewModel.h
...
struct BookstoreViewModel : BookstoreViewModelT<BookstoreViewModel>
{
BookstoreViewModel()
{
m_bookSkus = winrt::single_threaded_observable_vector<Bookstore::BookSku>();
m_bookSkus.Append(winrt::make<Bookstore::implementation::BookSku>(L"To Kill A Mockingbird"));
}
Windows::Foundation::Collections::IObservableVector<Bookstore::BookSku> BookSkus();
{
return m_bookSkus;
}
private:
Windows::Foundation::Collections::IObservableVector<Bookstore::BookSku> m_bookSkus;
};
...
Aby uzyskać więcej informacji, zobacz kontrolki elementów XAML; wiązanie z kolekcją C++/WinRTi kolekcjami za pomocą języka C++/WinRT.
Udostępnianie źródła danych dla znaczników XAML (przed C++/WinRT 2.0.190530.8)
Powiązanie danych XAML wymaga, aby źródło elementów implementowało IIterable<IInspectable>, a także jedną z następujących kombinacji interfejsów.
- IObservableVector<IInspectable>
- IBindableVector i INotifyCollectionChanged
- IBindableVector i IBindableObservableVector
- IBindableVector sam w sobie (nie będzie reagować na zmiany)
- IVector<IInspectable>
- IBindableIterable (będzie iterować i zapisywać elementy w prywatnej kolekcji)
Nie można wykryć interfejsu ogólnego, takiego jak IVector<T>, w czasie wykonywania. Każdy IVector<T> ma inny identyfikator interfejsu (IID), który jest funkcją T. Każdy deweloper może rozszerzyć kolekcję T dowolnie, więc oczywiście kod powiązania XAML nigdy nie może znać pełnego zestawu, o który można zapytać. To ograniczenie nie jest problemem dla języka C#, ponieważ każdy obiekt CLR implementujący IEnumerable T< automatycznie implementuje IEnumerable>. Na poziomie ABI oznacza to, że każdy obiekt, który to implementuje IObservableVector<T>, automatycznie implementuje IObservableVector<IInspectable>.
Język C++/WinRT nie oferuje tej gwarancji. Jeśli klasa środowiska uruchomieniowego C++/WinRT implementuje IObservableVector<T>, nie możemy założyć, że implementacja IObservableVector<IInspectable> jest również w jakiś sposób dostępna.
W związku z tym poniżej przedstawiono sposób, w jaki poprzedni przykład będzie musiał wyglądać.
...
runtimeclass BookstoreViewModel
{
// This is really an observable vector of BookSku.
Windows.Foundation.Collections.IObservableVector<Object> BookSkus{ get; };
}
I implementacja.
// BookstoreViewModel.h
...
struct BookstoreViewModel : BookstoreViewModelT<BookstoreViewModel>
{
BookstoreViewModel()
{
m_bookSkus = winrt::single_threaded_observable_vector<Windows::Foundation::IInspectable>();
m_bookSkus.Append(winrt::make<Bookstore::implementation::BookSku>(L"To Kill A Mockingbird"));
}
// This is really an observable vector of BookSku.
Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> BookSkus();
{
return m_bookSkus;
}
private:
Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> m_bookSkus;
};
...
Jeśli musisz uzyskać dostęp do obiektów w m_bookSkus, musisz przekonwertować je z powrotem na Bookstore::BookSku.
Widget MyPage::BookstoreViewModel(winrt::hstring title)
{
for (auto&& obj : m_bookSkus)
{
auto bookSku = obj.as<Bookstore::BookSku>();
if (bookSku.Title() == title) return bookSku;
}
return nullptr;
}
Klasy pochodne
Aby dziedziczyć z klasy środowiska uruchomieniowego, klasa bazowa musi być komponowalna. Język C# nie wymaga wykonania żadnych specjalnych kroków w celu skomponowania klas, ale język C++/WinRT to wymaga. Aby wskazać, że klasa ma być używana jako klasa bazowa, należy użyć słowa kluczowego unsealed.
unsealed runtimeclass BasePage : Windows.UI.Xaml.Controls.Page
{
...
}
runtimeclass DerivedPage : BasePage
{
...
}
W pliku nagłówkowym dla typu implementacji należy dołączyć plik nagłówka klasy bazowej przed dołączeniem automatycznie wygenerowanego nagłówka dla klasy pochodnej. W przeciwnym razie zostaną wyświetlone błędy, takie jak "Niedozwolone użycie tego typu jako wyrażenie".
// DerivedPage.h
#include "BasePage.h" // This comes first.
#include "DerivedPage.g.h" // Otherwise this header file will produce an error.
namespace winrt::MyNamespace::implementation
{
struct DerivedPage : DerivedPageT<DerivedPage>
{
...
}
}
Ważne interfejsy API
Tematy pokrewne
- samouczki języka C#
- C++/WinRT
- Dogłębne powiązanie danych