Přechod na C++/WinRT z C#

Návod

Pokud jste si toto téma přečetli dříve a vracíte se k němu s ohledem na konkrétní úkol, můžete přejít na obsah Najít na základě úkolu, který provádíte v tomto tématu.

Toto téma komplexně kataloguje technické podrobnosti týkající se přenosu zdrojového kódu do projektu C# do jeho ekvivalentu v C++/WinRT.

Případovou studii portování jednoho z ukázkových projektů platformy Univerzální platforma Windows (UPW) najdete v doprovodném tématu Portování ukázky Schránka z jazyka C# do C++/WinRT. Praxi a zkušenosti s portováním můžete získat tak, že budete postupovat podle tohoto návodu a v jeho průběhu si sami přeportujete ukázku.

Jak se připravit a co očekávat

Případová studie Přenos ukázky Schránka z jazyka C# do C++/WinRT ilustruje příklady druhů rozhodnutí týkajících se návrhu softwaru, která budete činit při převodu projektu na C++/WinRT. Proto je vhodné se připravit na přenos tím, že získáte solidní znalosti o tom, jak stávající kód funguje. Tak získáte dobrý přehled o funkčnosti aplikace i struktuře kódu a rozhodnutí, která pak učiníte, vás budou vždy posouvat kupředu správným směrem.

Z hlediska toho, jaké druhy změn přenosu se mají očekávat, můžete je seskupit do čtyř kategorií.

  • Portujte projekci jazyka. prostředí Windows Runtime (WinRT) se projektuje do různých programovacích jazyků. Každá z těchto jazykových projekcí je navržena tak, aby působila přirozeně pro daný programovací jazyk. U jazyka C# jsou některé typy prostředí Windows Runtime projektovány jako typy .NET. Takže například přeložíte System.Collections.Generic.IReadOnlyList<T> zpět na Windows. Foundation.Collections.IVectorView<T>. V jazyce C# jsou některé operace prostředí Windows Runtime promítnuty jako pohodlné jazykové funkce jazyka C#. Příkladem je, že v jazyce C# použijete syntaxi operátoru += k registraci delegáta zpracování událostí. Budete tedy převádět jazykové konstrukce, jako je tato, na základní operaci, která se provádí (v tomto příkladu registrace události).
  • Syntaxe jazyka portu Mnoho z těchto změn je jednoduchých mechanických transformací, nahrazení jednoho symbolu za druhý. Například změna tečky (.) na dvojtečku (::).
  • Procedura jazyka portu Některé z nich mohou být jednoduché, opakované změny (například myObject.MyProperty na myObject.MyProperty()). Jiné potřebují hlubší změny (například portování procedury, která zahrnuje použití System.Text.StringBuilder na takový, který zahrnuje použití std::wostringstream).
  • Přenos úloh souvisejících s C++/WinRT O některé podrobnosti rozhraní prostředí Windows Runtime se na pozadí automaticky stará jazyk C#. Tyto podrobnosti se provádějí explicitně v jazyce C++/WinRT. Příkladem je použití .idl souboru k definování tříd modulu runtime.

Po následujícím indexu založeném na úlohách jsou zbývající části tohoto tématu strukturovány podle výše uvedené taxonomie.

Vyhledání obsahu na základě úkolu, který provádíte

Úkol Content
Vytvoření komponenty prostředí Windows Runtime (WRC) Některé funkce lze realizovat (nebo volat určitá rozhraní API) pouze pomocí jazyka C++. Tuto funkci můžete zohlednit v jazyce C++/WinRT WRC a pak využívat WRC z aplikace jazyka C# (například). Viz Komponenty prostředí Windows Runtime s C++/WinRT a Pokud v komponentě prostředí Windows Runtime vytváříte běhovou třídu.
Přenést asynchronní metodu Je vhodné, aby prvním řádkem asynchronní metody ve třídě runtime C++/WinRT bylo auto lifetime = get_strong(); (viz Bezpečný přístup k ukazateli this v korutině člena třídy).

Portování z Task, viz Asynchronní akce.
Přenos z Task<T>, viz asynchronní operace.
Portování z async void, viz Metoda Fire-and-forget.
Přenést třídu Nejprve určete, zda třída musí být třídou modulu runtime, nebo zda může být běžnou třídou. Abyste se mohli rozhodnout, podívejte se na začátek rozhraní API pro vytváření pomocí C++/WinRT. Pak se podívejte na následující tři řádky.
Port třídy modulu runtime Třída, která sdílí funkce mimo aplikaci C++ nebo třídu, která se používá v datové vazbě XAML. Viz Pokud vytváříte běhovou třídu v komponentě prostředí Windows Runtime nebo pokud vytváříte běhovou třídu, na kterou se bude odkazovat ve vašem uživatelském rozhraní XAML.

Tyto odkazy tento popis podrobněji popisují, ale třída modulu runtime musí být deklarována v IDL. Pokud váš project již obsahuje soubor IDL (napříkladProject.idl), doporučujeme deklarovat všechny nové třídy modulu runtime v tomto souboru. V IDL deklarujte všechny metody a datové členy, které se použijí mimo vaši aplikaci nebo které se použijí v XAML. Po aktualizaci souboru IDL projekt znovu sestavte a podívejte se na vygenerované soubory stub (.h a .cpp) ve složce Generated Files vašeho projektu (v okně Průzkumník řešení se s vybraným uzlem projektu ujistěte, že je zapnutá možnost Zobrazit všechny soubory). Porovnejte stub soubory se soubory, které již v projektu máte, a podle potřeby přidejte soubory nebo přidejte či aktualizujte signatury funkcí. Syntaxe souboru stubů je vždy správná, proto doporučujeme jej používat, abyste minimalizovali chyby sestavení. Jakmile stuby ve vašem projektu odpovídají těm ve stub souborech, můžete je začít implementovat přenesením kódu v jazyce C#.
Převod běžné třídy Viz pokud nevytváříte třídu runtime.
Autor IDL Úvod do jazyka Microsoft Interface Definition Language 3.0
Pokud vytváříte třídu runtime, na kterou bude odkazovat vaše uživatelské rozhraní XAML
Využívání objektů z revizí XAML
Definování tříd modulu runtime v IDL
Přenést kolekci Kolekce s C++/WinRT
Zpřístupnění zdroje dat pro kód XAML
Asociativní kontejner
Přístup ke členu vektoru
Přenést událost Delegát obslužné metody události jako člen třídy
Odvolání delegáta obslužné rutiny události
Přenést metodu Z jazyka C#: private async void SampleButton_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e) { ... }
Do souboru C++/WinRT .h : fire_and_forget SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&);
Do souboru C++/WinRT .cpp : fire_and_forget OcrFileImage::SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&) {...}
Řetězce portů Zpracování řetězců v C++/WinRT
ToString
Vytváření řetězců
Boxing and unboxing a string
Převod typu (přetypování typu) C#: o.ToString()
C++/WinRT: to_hstring(static_cast<int>(o))
Viz Také toString.

C#: (Value)o
C++/WinRT: unbox_value<Value>(o)
Vyvolá se v případě, že se nepovede rozbalení. Viz Také Boxing a unboxing.

C#: o as Value? ?? fallback
C++/WinRT: unbox_value_or<Value>(o, fallback)
Vrátí náhradní hodnotu, pokud se rozbalení hodnoty nezdaří. Viz Také Boxing a unboxing.

C#: (Class)o
C++/WinRT: o.as<Class>()
Vyvolá chybu v případě selhání převodu.

C#: o as Class
C++/WinRT: o.try_as<Class>()
Vrátí hodnotu null, pokud se převod nezdaří.

Změny, které zahrnují projekci jazyka

Category jazyk C# C++/WinRT Viz také
Netypovaný objekt object, nebo System.Object Windows::Foundation::IInspectable Portování metody EnableClipboardContentChangedNotifications
Obory názvů pro projekce using System; using namespace Windows::Foundation;
using System.Collections.Generic; using namespace Windows::Foundation::Collections;
Velikost kolekce collection.Count collection.Size() Portování metody BuildClipboardFormatsOutputString
Typický typ kolekce IList<T> a Add pro přidání prvku. IVector<T> a Append pro přidání prvku. Pokud někde používáte std::vector, přidejte prvek pomocí push_back.
Typ kolekce jen pro čtení IReadOnlyList<T> IVectorView<T> Portování metody BuildClipboardFormatsOutputString
Delegát pro obslužnou rutinu události jako člen třídy myObject.EventName += Handler; token = myObject.EventName({ get_weak(), &Class::Handler }); Portování metody EnableClipboardContentChangedNotifications
Odvolat delegáta obslužné rutiny události myObject.EventName -= Handler; myObject.EventName(token); Portování metody EnableClipboardContentChangedNotifications
Asociativní kontejner IDictionary<K, V> IMap<K, V>
Přístup k členu vektoru x = v[i];
v[i] = x;
x = v.GetAt(i);
v.SetAt(i, x);

Zaregistrovat nebo zrušit registraci obslužné rutiny pro událost

V jazyce C++/WinRT máte několik syntaktických možností registrace nebo odvolání delegáta obslužné rutiny události, jak je popsáno v tématu Zpracování událostí pomocí delegátů v C++/WinRT. Viz také Přenos metody EnableClipboardContentChangedNotifications.

Někdy se například stane, že příjemce události (objekt zpracovávající událost) bude zničen, budete chtít odvolat obslužnou rutinu události, aby zdroj události (objekt vyvolávácí událost) nevolal do zničeného objektu. Viz Odvolání registrovaného delegáta. V takových případech vytvořte členskou proměnnou event_token pro vaše obslužné rutiny událostí. Příklad naleznete v části Portování metody EnableClipboardContentChangedNotifications.

Obslužnou rutinu události můžete také zaregistrovat v kódu XAML.

<Button x:Name="OpenButton" Click="OpenButton_Click" />

V jazyce C# může být vaše metoda OpenButton_Click soukromá a XAML ji stále bude moct připojit k události ButtonBase.Click vyvolanou OpenButtonem.

V jazyce C++/WinRT musí být vaše metoda OpenButton_Click ve vašem typu implementace veřejná, pokud ji chcete zaregistrovat v kódu XAML. Pokud zaregistrujete obslužnou rutinu události pouze v imperativním kódu, obslužná rutina události nemusí být veřejná.

namespace winrt::MyProject::implementation
{
    struct MyPage : MyPageT<MyPage>
    {
        void OpenButton_Click(
            winrt::Windows::Foundation::IInspectable const& sender,
            winrt::Microsoft::UI::Xaml::RoutedEventArgs const& args);
    }
};

Případně můžete stránku XAML, která provádí registraci, prohlásit jako friend vašeho typu implementace a metodu OpenButton_Click jako soukromou.

namespace winrt::MyProject::implementation
{
    struct MyPage : MyPageT<MyPage>
    {
    private:
        friend MyPageT;
        void OpenButton_Click(
            winrt::Windows::Foundation::IInspectable const& sender,
            winrt::Microsoft::UI::Xaml::RoutedEventArgs const& args);
    }
};

Jedním z konečných scénářů je situace, kdy projekt C#, který portujete, vytvoří vazbu na obslužnou rutinu události z kódu (další pozadí tohoto scénáře najdete v tématu Funkce v x:Bind).

<Button x:Name="OpenButton" Click="{x:Bind OpenButton_Click}" />

Mohli byste jen změnit tento kód na jednodušší Click="OpenButton_Click". Nebo, pokud chcete, můžete toto značení ponechat beze změny. Jediné, co musíte udělat, abyste to podpořili, je deklarovat obsluhu události v IDL.

void OpenButton_Click(Object sender, Microsoft.UI.Xaml.RoutedEventArgs e);

Note

Deklarujte funkci jako void i když ji implementujete jako fire and forget.

Změny, které zahrnují syntaxi jazyka

Category jazyk C# C++/WinRT Viz také
Modifikátory přístupu public \<member\> public:
    \<member\>
Přenos metody Button_Click
Přístup k datovému členu this.variable this->variable  
Asynchronní akce async Task ... IAsyncAction ... Rozhraní IAsyncAction, souběžnost a asynchronní operace s C++/WinRT
Asynchronní operace async Task<T> ... IAsyncOperation<T> ... Rozhraní IAsyncOperation, souběžnost a asynchronní operace s C++/WinRT
Metoda „fire-and-forget“ (tj. asynchronní) async void ... winrt::fire_and_forget ... Přenos metody CopyButton_Click, aktivace a zapomenutí
Přístup k výčtové konstantě E.Value E::Value Portování metody DisplayChangedFormats
Kooperativní čekání await ... co_await ... Přenos metody CopyButton_Click
Kolekce projektovaných typů jako soukromé pole private List<MyRuntimeClass> myRuntimeClasses = new List<MyRuntimeClass>(); std::vector
<MyNamespace::MyRuntimeClass>
m_myRuntimeClasses;
Vytvoření identifikátoru 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} };
Oddělovač oboru názvů A.B.T A::B::T
Null null nullptr Přenos metody UpdateStatus
Získání objektu typu typeof(MyType) winrt::xaml_typename<MyType>() Přenos vlastnosti Scenarios
Deklarace parametru pro metodu MyType MyType const& Předávání parametrů
Deklarace parametru pro asynchronní metodu MyType MyType Předávání parametrů
Volání statické metody T.Method() T::Method()
Řetězce string, nebo System.String winrt::hstring Zpracování řetězců v C++/WinRT
Řetězcový literál "a string literal" L"a string literal" Přenos konstruktoru, aktuálního a FEATURE_NAME
Odvozený (nebo dedukovaný) typ var auto Portování metody BuildClipboardFormatsOutputString
Using-direktiva using A.B.C; using namespace A::B::C; Přenos konstruktoru, aktuálního a FEATURE_NAME
Doslovné nebo nezpracované řetězcové literály @"verbatim string literal" LR"(raw string literal)" Přenos metody DisplayToast

Note

Pokud soubor hlavičky neobsahuje direktivu using namespace pro daný obor názvů, budete muset plně kvalifikovat všechny názvy typů pro tento obor názvů, nebo je alespoň kvalifikovat dostatečně, aby je kompilátor mohl najít. Příklad najdete v části Portování metody DisplayToast.

Přenos tříd a členů

Pro každý typ jazyka C# budete muset rozhodnout, jestli se má portovat na typ prostředí Windows Runtime, nebo do běžné třídy,struktury/výčtu jazyka C++. Další informace a podrobné příklady, které ukazují, jak tato rozhodnutí učinit, viz Přenos ukázky Clipboard z jazyka C# do C++/WinRT.

Vlastnost jazyka C# se obvykle stává přistupující funkcí, funkcí mutátoru a záložním datovým členem. Další informace a příklad najdete v tématu Přenos vlastnosti IsClipboardContentChangedEnabled.

U nestatického pole je nastavte jako datové členy vašeho typu implementace.

Statické pole jazyka C# se převede na statickou přístupovou a/nebo nastavovací funkci v C++/WinRT. Další informace a příklad naleznete v článku Přenesení konstruktoru, Current a FEATURE_NAME.

U členských funkcí budete muset znovu rozhodnout, jestli patří do IDL, nebo jestli se jedná o veřejnou nebo soukromou členskou funkci vašeho typu implementace. Další informace a příklady, které vám pomohou se rozhodnout, najdete v dokumentaci IDL pro typ MainPage.

Převod značkovacího kódu XAML a souborů prostředků

V případě přenosu ukázky schránky do C++/WinRT z C# jsme mohli použít stejný kód XAML (včetně zdrojů) a souborů prostředků v rámci C# a projektu C++/WinRT. V některých případech budou k dosažení tohoto cíle nezbytné úpravy značkování. Viz Zkopírování XAML a stylů potřebných k dokončení portování MainPage.

Změny, které zahrnují postupy v rámci jazyka

Category jazyk C# C++/WinRT Viz také
Správa doby života v asynchronní metodě N/A auto lifetime{ get_strong() }; nebo
auto lifetime = get_strong();
Přenos metody CopyButton_Click
Vyřazení using (var t = v) auto t{ v };
t.Close(); // or let wrapper destructor do the work
Přenos metody CopyImage
Vytvořit objekt new MyType(args) MyType{ args } nebo
MyType(args)
Přenos vlastnosti Scenarios
Vytvoření neinicializovaného odkazu MyType myObject; MyType myObject{ nullptr }; nebo
MyType myObject = nullptr;
Přenos konstruktoru, aktuálního a FEATURE_NAME
Vytvoření objektu do proměnné pomocí args var myObject = new MyType(args); auto myObject{ MyType{ args } }; nebo
auto myObject{ MyType(args) }; nebo
auto myObject = MyType{ args }; nebo
auto myObject = MyType(args); nebo
MyType myObject{ args }; nebo
MyType myObject(args);
Přenos metody Footer_Click
Vytvoření objektu do proměnné bez args var myObject = new T(); MyType myObject; Portování metody BuildClipboardFormatsOutputString
Zkratka inicializace objektu var p = new FileOpenPicker{
    ViewMode = PickerViewMode.List
};
FileOpenPicker p;
p.ViewMode(PickerViewMode::List);
Hromadná vektorová operace var p = new FileOpenPicker{
    FileTypeFilter = { ".png", ".jpg", ".gif" }
};
FileOpenPicker p;
p.FileTypeFilter().ReplaceAll({ L".png", L".jpg", L".gif" });
Přenos metody CopyButton_Click
Iterace nad kolekcí foreach (var v in c) for (auto&& v : c) Portování metody BuildClipboardFormatsOutputString
Zachycení výjimky catch (Exception ex) catch (winrt::hresult_error const& ex) Přenos metody PasteButton_Click
Podrobnosti o výjimce ex.Message ex.message() Přenos metody PasteButton_Click
Získání hodnoty vlastnosti myObject.MyProperty myObject.MyProperty() Přenos metody NotifyUser
Nastavení hodnoty vlastnosti myObject.MyProperty = value; myObject.MyProperty(value);
Zvýšení hodnoty vlastnosti myObject.MyProperty += v; myObject.MyProperty(thing.Property() + v);
Pro řetězce přepněte na StringBuilder.
ToString() myObject.ToString() winrt::to_hstring(myObject) Tostring()
Jazykový řetězec na řetězec prostředí Windows Runtime N/A winrt::hstring{ s }
Vytváření řetězců StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Vytváření řetězců
Interpolace řetězců $"{i++}) {s.Title}" winrt::to_hstring a/nebo winrt::hstring::operator+ Portování metody OnNavigatedTo
Prázdný řetězec pro porovnání System.String.Empty winrt::hstring::empty Přenos metody UpdateStatus
Vytvořte prázdný řetězec var myEmptyString = String.Empty; winrt::hstring myEmptyString{ L"" };
Operace se slovníkem map[k] = v; // replaces any existing
v = map[k]; // throws if not present
map.ContainsKey(k)
map.Insert(k, v); // replaces any existing
v = map.Lookup(k); // throws if not present
map.HasKey(k)
Převod typu (vyvolání při selhání) (MyType)v v.as<MyType>() Přenos metody Footer_Click
Převod typu (v případě selhání null) v as MyType v.try_as<MyType>() Přenos metody PasteButton_Click
Elementy XAML s atributem x:Name jsou vlastnosti MyNamedElement MyNamedElement() Přenos konstruktoru, aktuálního a FEATURE_NAME
Přepněte na vlákno UI CoreDispatcher.RunAsync DispatcherQueue.TryEnqueue nebo winrt::resume_foreground Portování metody NotifyUser a portování metody HistoryAndRoaming
Konstrukce elementu uživatelského rozhraní v imperativním kódu na stránce XAML Viz konstrukce prvků uživatelského rozhraní Viz konstrukce prvků uživatelského rozhraní

Následující části obsahují podrobnější informace o některých položkách v tabulce.

Konstrukce prvků uživatelského rozhraní

Tyto příklady kódu ukazují vytvoření prvku uživatelského rozhraní v imperativním kódu stránky XAML.

var myTextBlock = new TextBlock()
{
    Text = "Text",
    Style = (Microsoft.UI.Xaml.Style)this.Resources["MyTextBlockStyle"]
};
TextBlock myTextBlock;
myTextBlock.Text(L"Text");
myTextBlock.Style(
    winrt::unbox_value<Microsoft::UI::Xaml::Style>(
        Resources().Lookup(
            winrt::box_value(L"MyTextBlockStyle")
        )
    )
);

ToString()

Typy jazyka C# poskytují metodu Object.ToString .

int i = 2;
var s = i.ToString(); // s is a System.String with value "2".

C++/WinRT přímo neposkytuje toto zařízení, ale můžete se obrátit na alternativy.

int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".

C++/WinRT také podporuje winrt::to_hstring pro omezený počet typů. Budete muset přidat přetížení pro všechny další typy, které chcete převést na řetězec.

Jazyk Převést int na řetězec Stringify – výčet
jazyk 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);

Při převodu výčtu na řetězec budete muset poskytnout implementaci 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));
        }
    }
}

Tyto řetězcové reprezentace jsou často implicitně využívány při datové vazbě.

<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>

Tyto vazby budou provádět winrt::to_hstring vázané vlastnosti. V případě druhého příkladu ( StatusEnum) musíte zadat vlastní přetížení winrt::to_hstring, jinak se zobrazí chyba kompilátoru.

Podívejte se také na portování metody Footer_Click.

Vytváření řetězců

Pro vytváření řetězců má jazyk C# integrovaný typ StringBuilder .

Category jazyk C# C++/WinRT
Vytváření řetězců StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Připojte řetězec prostředí Windows Runtime se zachováním hodnot null. builder.Append(s); builder << std::wstring_view{ s };
Přidání nového řádek builder.Append(Environment.NewLine); builder << std::endl;
Přístup k výsledku s = builder.ToString(); ws = builder.str();

Viz také Přenos metody BuildClipboardFormatsOutputString a přenos metody DisplayChangedFormats.

Spuštění kódu v hlavním vlákně uživatelského rozhraní

Tento příklad je převzat z vzorku skeneru čárových kódů.

Pokud chcete pracovat na hlavním vlákně uživatelského rozhraní v projektu C#, obvykle používáte metodu DispatcherQueue.TryEnqueue (nebo starší CoreDispatcher.RunAsync v UPW). Takto vypadá vzor v jazyce C#.

private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
{
    DispatcherQueue.TryEnqueue(() =>
    {
        // Do work on the main UI thread here.
    });
}

V jazyce C++/WinRT je mnohem jednodušší to vyjádřit. Všimněte si, že přijímáme parametry podle hodnoty podle předpokladu, že k nim budeme chtít přistupovat po prvním bodu pozastavení ( co_awaitv tomto případě). Další informace najdete v tématu Předávání parametrů.

winrt::fire_and_forget Watcher_Added(DeviceWatcher sender, winrt::DeviceInformation args)
{
    co_await DispatcherQueue();
    // Do work on the main UI thread here.
}

Pokud potřebujete pracovat s jinou prioritou než výchozí, podívejte se na funkci winrt::resume_foreground , která má přetížení, které má prioritu. Příklady kódu, které ukazují, jak čekat na volání winrt::resume_foreground, najdete v článku Programování s ohledem na afinitu vláken.

Definování tříd modulu runtime v IDL

Podívejte se na IDL pro typ MainPage a slučte své soubory .idl.

Zahrňte soubory hlaviček oboru názvů C++/WinRT Windows, které potřebujete.

V jazyce C++/WinRT musíte kdykoli, když chcete použít typ z některého jmenného prostoru Windows, zahrnout odpovídající soubor záhlaví jmenného prostoru Windows pro C++/WinRT. Příklad najdete v tématu Přenesení metody NotifyUser.

Boxování a rozbalování

Jazyk C# automaticky rozdělí skaláry do objektů. C++/WinRT vyžaduje, abyste explicitně volali funkci winrt::box_value . Oba jazyky vyžadují explicitní rozbalení. Viz Boxing and unboxing with C++/WinRT.

V následujících tabulkách použijeme tyto definice.

jazyk C# C++/WinRT
int i; int i;
string s; winrt::hstring s;
object o; IInspectable o;
Operation jazyk C# C++/WinRT
Boxování o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
Rozbalení i = (int)o;
s = (string)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

C++/CX a C# vyvolá výjimky, pokud se pokusíte rozbalit ukazatel null na typ hodnoty. C++/WinRT to považuje za programovací chybu a zhroutí se. V jazyce C++/WinRT použijte funkci winrt::unbox_value_or , pokud chcete zpracovat případ, kdy objekt není typu, o kterém jste si mysleli, že se jedná.

Scenario jazyk C# C++/WinRT
Rozbalte známé celé číslo i = (int)o; i = unbox_value<int>(o);
Pokud má o hodnotu null System.NullReferenceException Havárie
Pokud o není zabalený int System.InvalidCastException Havárie
Rozbalte int, použijte záložní hodnotu, pokud je null; jinak havarujte i = o != null ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Pokud je to možné, převeďte na typ int; ve všech ostatních případech použijte záložní variantu i = as int? ?? fallback; i = unbox_value_or<int>(o, fallback);

Příklad naleznete v části Portování metody OnNavigatedTo a Portování metody Footer_Click.

Boxing a unboxing řetězce

Řetězec je nějakým způsobem typ hodnoty a jiným způsobem typ odkazu. C# a C++/WinRT zachází s řetězci odlišně.

Typ ABI HSTRING je ukazatel na řetězec s počítáním referencí. Ale neodvozuje se od IInspectable, takže to není technicky objekt. Navíc hodnota HSTRING s hodnotou null představuje prázdný řetězec. Zabalení věcí, které nejsou odvozeny z IInspectable je provádí zabalením uvnitř IReference<T> a prostředí Windows Runtime poskytuje standardní implementaci ve formě PropertyValue objektu (vlastní typy jsou hlášeny jako PropertyType::OtherType).

Jazyk C# reprezentuje řetězec prostředí Windows Runtime jako referenční typ, zatímco C++/WinRT reprezentuje řetězec jako hodnotový typ. To znamená, že řetězec s hodnotou null s rámečkem může mít různé reprezentace v závislosti na tom, jak jste se tam dostali.

Behavior jazyk C# C++/WinRT
Prohlášení object o;
string s;
IInspectable o;
hstring s;
Kategorie typu řetězce Typ odkazu Typ hodnoty
null se mapuje jako HSTRING "" hstring{}
Jsou hodnoty null a "" identické? No Yes
Platnost hodnoty null s = null;
s.Length vyvolá NullReferenceException
s = hstring{};
s.size() == 0 (platné)
Pokud k objektu přiřadíte řetězec null. o = (string)null;
o == null
o = box_value(hstring{});
o != nullptr
Pokud k objektu přiřadíte "" o = "";
o != null
o = box_value(hstring{L""});
o != nullptr

Základní boxování a rozbalení.

Operation jazyk C# C++/WinRT
Zabalit řetězec o = s;
Prázdný řetězec se stane objektem, který není null.
o = box_value(s);
Prázdný řetězec se stane objektem, který není null.
Rozbalení známého řetězce s = (string)o;
Objekt Null se stane řetězcem null.
InvalidCastException, pokud nejde o řetězec.
s = unbox_value<hstring>(o);
Objekt null selže.
Pokud to není řetězec, aplikace spadne.
Rozbalení možného řetězce s = o as string;
Objekt null nebo ne-řetězec se změní na řetězec null.

OR

s = o as string ?? fallback;
Hodnota null nebo hodnota, která není řetězec, se nahradí náhradní hodnotou.
Prázdný řetězec zachován.
s = unbox_value_or<hstring>(o, fallback);
Hodnota null nebo hodnota, která není řetězec, použije záložní hodnotu.
Prázdný řetězec je zachován.

Zpřístupnění třídy rozšíření značek {Binding}

Pokud máte v úmyslu použít rozšíření značek {Binding} k vytvoření vazby dat s vaším datovým typem, podívejte se na objekt vazby deklarovaný pomocí {Binding}.

Využívání objektů z revizí XAML

V projektu jazyka C# můžete využívat soukromé členy a pojmenované elementy z kódu XAML. V jazyce C++/WinRT se ale všechny entity spotřebované pomocí rozšíření značek XAML {x:Bind} musí veřejně zveřejnit v IDL.

Také se při svázání s booleovskou hodnotou v jazyce C# zobrazí true nebo false, ale v jazyce C++/WinRT se zobrazí Windows.Foundation.IReference`1<Boolean>.

Další informace a příklady kódu najdete v tématu Využívání objektů z revizí.

Zpřístupnění zdroje dat pro kód XAML

V jazyce C++/WinRT verze 2.0.190530.8 nebo novější vytváří winrt::single_threaded_observable_vector pozorovatelný vektor, který podporuje jak IObservableVector<T>, tak IObservableVector<IInspectable>. Příklad najdete v tématu Přenos vlastnosti Scenarios.

Soubor MIDL (.idl) můžete vytvořit takto (viz také Rozdělení tříd modulu runtime do souborů MIDL (.idl)).

namespace Bookstore
{
    runtimeclass BookSku { ... }

    runtimeclass BookstoreViewModel
    {
        Windows.Foundation.Collections.IObservableVector<BookSku> BookSkus{ get; };
    }

    runtimeclass MainPage : Microsoft.UI.Xaml.Controls.Page
    {
        MainPage();
        BookstoreViewModel MainViewModel{ get; };
    }
}

A implementujte takhle.

// 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;
};
...

Další informace najdete v tématu Ovládací prvky položek XAML, vazba na kolekci C++/WinRT a kolekce pomocí C++/WinRT.

Zpřístupnění zdroje dat pro kód XAML (před C++/WinRT 2.0.190530.8)

Datová vazba XAML vyžaduje, aby zdroj položek implementuje IIterable<IInspectable>, stejně jako jednu z následujících kombinací rozhraní.

  • IObservableVector<IInspectable>
  • IBindableVector a INotifyCollectionChanged
  • IBindableVector a IBindableObservableVector
  • IBindableVector sám (nebude reagovat na změny)
  • IVector<IInspectable>
  • IBindableIterable (bude iterovat a ukládat prvky do privátní kolekce)

Obecné rozhraní, jako je IVector<T> , nelze rozpoznat za běhu. Každý IVector<T> má jiný identifikátor rozhraní (IID), což je funkce T. Libovolný vývojář může libovolně rozšířit sadu T , takže jasně kód vazby XAML nikdy nezná úplnou sadu pro dotazování. Toto omezení není problém pro jazyk C#, protože každý objekt CLR, který implementuje IEnumerable<T> automaticky implementuje IEnumerable. Na úrovni ABI to znamená, že každý objekt, který implementuje IObservableVector<T> automaticky implementuje IObservableVector<IInspectable>.

C++/WinRT nenabízí tuto záruku. Pokud třída modulu runtime C++/WinRT implementuje IObservableVector<T>, pak nemůžeme předpokládat, že implementace IObservableVector<IInspectable> je nějakým způsobem poskytována.

V důsledku toho bude předchozí příklad muset vypadat takto.

...
runtimeclass BookstoreViewModel
{
    // This is really an observable vector of BookSku.
    Windows.Foundation.Collections.IObservableVector<Object> BookSkus{ get; };
}

A její implementace.

// 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;
};
...

Pokud potřebujete přistupovat k objektům v m_bookSkus, budete je muset QI vrátit zpět do Knihkupectví::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;
}

Odvozené třídy

Aby bylo možné dědit z běhové třídy, musí být základní třída komponovatelná. Jazyk C# nevyžaduje, abyste podnikli nějaké speciální kroky, aby vaše třídy byly kompozibilní, ale v C++/WinRT ano. Nezapečetěné klíčové slovo použijete k označení, že chcete, aby vaše třída byla použitelná jako základní třída.

unsealed runtimeclass BasePage : Microsoft.UI.Xaml.Controls.Page
{
    ...
}
runtimeclass DerivedPage : BasePage
{
    ...
}

V hlavičkovém souboru pro váš typ implementace musíte zahrnout hlavičkový soubor základní třídy předtím, než zahrnete automaticky generovaný hlavičkový soubor odvozené třídy. V opačném případě se zobrazí chyby typu Neplatné použití tohoto typu jako výrazu.

// 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>
    {
        ...
    }
}

Důležitá rozhraní API