Flytta till C++/WinRT från C#

Tip

Om du har läst det här avsnittet tidigare och du återvänder till det med en viss uppgift i åtanke kan du gå till avsnittet Hitta innehåll baserat på den uppgift du utför i det här avsnittet.

Det här avsnittet innehåller en omfattande katalog över den tekniska information som ingår i porteringen av källkoden i ett C#- projekt till dess motsvarighet i C++/WinRT.

En fallstudie om att portera ett av de Universal Windows Platform (UWP) appexemplen finns i det tillhörande ämnet Porting the Clipboard sample to C++/WinRT from C#. Du kan öva på portering och skaffa dig erfarenhet genom att följa den genomgången och själv portera exemplet under tiden.

Så här förbereder du dig och vad du kan förvänta dig

Fallstudien Porting the Clipboard sample to C++/WinRT from C# illustrerar exempel på de typer av beslut om programvarudesign som du ska fatta när du porterar ett projekt till C++/WinRT. Därför är det en bra idé att förbereda för portning genom att få en gedigen förståelse för hur den befintliga koden fungerar. På så sätt får du en bra översikt över appens funktioner och kodens struktur, och sedan kommer de beslut du fattar alltid att föra dig framåt och i rätt riktning.

När det gäller vilka typer av portningsändringar som kan förväntas kan du gruppera dem i fyra kategorier.

  • Porta språkprojektionen. Windows Runtime (WinRT) projiceras till olika programmeringsspråk. Var och en av dessa språkprojektioner är utformad för att känna sig idiomatisk för programmeringsspråket i fråga. För C# beräknas vissa Windows Runtime typer som .NET typer. Du kommer till exempel att översätta System.Collections.Generic.IReadOnlyList<T> tillbaka till Windows. Foundation.Collections.IVectorView<T>. Även i C# återges vissa Windows Runtime-operationer som praktiska språkfunktioner i C#. Ett exempel är att du i C# använder operatorsyntaxen += för att registrera ett ombud för händelsehantering. Så du kommer att översätta språkfunktioner som den tillbaka till den grundläggande åtgärd som utförs (händelseregistrering, i det här exemplet).
  • Syntax för portspråk. Många av dessa ändringar är enkla mekaniska transformeringar som ersätter en symbol för en annan. Du kan till exempel ändra punkt (.) till dubbelkolon (::).
  • Procedur för portspråk. Vissa av dessa kan vara enkla, repetitiva ändringar (till exempel myObject.MyProperty till myObject.MyProperty()). Andra behöver djupare ändringar (till exempel portning av en procedur som innebär användning av System.Text.StringBuilder till en som innebär användning av std::wostringstream).
  • Portningsrelaterade uppgifter som är specifika för C++/WinRT. Vissa detaljer om Windows Runtime tas hand om implicit av C#, i bakgrunden. De detaljerna hanteras uttryckligen i C++/WinRT. Ett exempel är att du använder en .idl-fil för att definiera dina runtime-klasser.

Efter det aktivitetsbaserade indexet som följer struktureras resten av avsnitten i det här avsnittet enligt taxonomi ovan.

Hitta innehåll baserat på den uppgift du utför

Uppgift Content
Skapa en Windows Runtime komponent (WRC) Vissa funktioner kan endast uppnås (eller vissa API:er anropas) med C++. Du kan räkna in den funktionen i en C++/WinRT WRC och sedan använda WRC från (till exempel) en C#-app. Se Windows Runtime-komponenter med C++/WinRT och Om du skapar en runtime-klass i en Windows Runtime-komponent.
Migrera en asynkron metod Det är en bra idé att den första raden i en asynkron metod i en C++/WinRT-körningsklass ska vara auto lifetime = get_strong(); (se Säker åtkomst till den här pekaren i en coroutine för klassmedlemmar).

Vid portering från Task, se Asynkron åtgärd.
Portering från Task<T>, se asynkron åtgärd.
Portning från async void, se Fire-and-forget-metoden.
Portera en klass Avgör först om klassen måste vara en runtimeklass eller om den kan vara en vanlig klass. Information om hur du bestämmer dig för det finns i början av Api:er för författare med C++/WinRT. Se sedan de följande tre raderna nedan.
Portera en körningsklass En klass som delar funktioner utanför C++-appen eller en klass som används i XAML-databindning. Se Om du redigerar en körningsklass i en Windows Runtime komponent eller Om du redigerar en körningsklass som ska refereras i XAML-användargränssnittet.

Dessa länkar beskriver detta mer i detalj, men en runtime-klass måste deklareras i IDL. Om din project redan innehåller en IDL-fil (till exempel Project.idl), rekommenderar vi att du deklarerar alla nya körningsklasser i filen. I IDL deklarerar du alla metoder och datamedlemmar som ska användas utanför din app, eller som kommer att användas i XAML. När du har uppdaterat IDL-filen återskapar du och tittar på de genererade stub-filerna (.h och .cpp) i projektets Generated Files mapp (I Prieskumník riešení, med projektnoden markerad, kontrollerar du att Visa alla filer är aktiverat). Jämför stub-filerna med de filer som redan finns i projektet, lägga till filer eller lägga till/uppdatera funktionssignaturer efter behov. Stub-filsyntaxen är alltid korrekt, så vi rekommenderar att du använder den för att minimera byggfel. När stubsna i projektet matchar dem i stub-filerna kan du implementera dem genom att portera C#-koden över.
Portera en vanlig klass Se Om du inte skriver en runtime-klass.
Författar-IDL Introduktion till Microsoft Interface Definition Language 3.0
Om du skapar en körningsklass som du refererar till i ditt XAML-användargränssnitt
Använda objekt från XAML-kod
Definiera dina runtime-klasser i IDL
Porta en samling Samlingar med C++/WinRT
Göra en datakälla tillgänglig för XAML-markering
Associativ container
Vektormedlemsåtkomst
Migrera en händelse Händelsehanterardelegat som klassmedlem
Återkalla händelsehanterardelegat
Portera en metod Från C#: private async void SampleButton_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e) { ... }
Till C++/WinRT-filen .h : fire_and_forget SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&);
Till C++/WinRT-filen .cpp : fire_and_forget OcrFileImage::SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&) {...}
Portsträngar Stränghantering i C++/WinRT
ToString
Strängkonstruktion
Boxning och avboxning av en sträng
Typkonvertering (typgjutning) C#: o.ToString()
C++/WinRT: to_hstring(static_cast<int>(o))
Se även ToString.

C#: (Value)o
C++/WinRT: unbox_value<Value>(o)
Genererar ett undantag om avboxningen misslyckas. Se även Boxning och avboxning.

C#: o as Value? ?? fallback
C++/WinRT: unbox_value_or<Value>(o, fallback)
Returnerar reservvärdet om uppackningen misslyckas. Se även Boxning och avboxning.

C#: (Class)o
C++/WinRT: o.as<Class>()
Genererar om konverteringen misslyckas.

C#: o as Class
C++/WinRT: o.try_as<Class>()
Returnerar null om konverteringen misslyckas.

Ändringar som omfattar språkprojektionen

Category C# C++/WinRT Se även
Objekt utan typ object, eller System.Object Windows::Foundation::IInspectable Portering av metoden EnableClipboardContentChangedNotifications
Projektionsnamnområden using System; using namespace Windows::Foundation;
using System.Collections.Generic; using namespace Windows::Foundation::Collections;
Storleken på en samling collection.Count collection.Size() Portering av metoden BuildClipboardFormatsOutputString
Typisk samlingstyp IList<T> och Lägg till för att lägga till ett element. IVector<T> och Lägg till för att lägga till ett element. Om du använder en std::vector någonstans, använd då push_back för att lägga till ett element.
Skrivskyddad samlingstyp IReadOnlyList<T> IVectorView<T> Portering av metoden BuildClipboardFormatsOutputString
Händelsehanterardelegat som klassmedlem myObject.EventName += Handler; token = myObject.EventName({ get_weak(), &Class::Handler }); Portering av metoden EnableClipboardContentChangedNotifications
Ta bort händelsehanterardelegat myObject.EventName -= Handler; myObject.EventName(token); Portering av metoden EnableClipboardContentChangedNotifications
Associativ behållare IDictionary<K, V> IMap<K, V>
Vektormedlemsåtkomst x = v[i];
v[i] = x;
x = v.GetAt(i);
v.SetAt(i, x);

Registrera/återkalla en händelsehanterare

I C++/WinRT har du flera syntaktiska alternativ för att registrera/återkalla ett händelsehanterardelegat enligt beskrivningen i Hantera händelser med hjälp av ombud i C++/WinRT. Se även Portning av metoden EnableClipboardContentChangedNotifications.

Ibland, till exempel när en händelsemottagare (ett objekt som hanterar en händelse) håller på att förstöras, vill du återkalla en händelsehanterare så att händelsekällan (objektet som lyfter händelsen) inte anropar till ett förstört objekt. Se Återkalla ett registrerat ombud. I så fall skapar du en event_token medlemsvariabel för dina händelsehanterare. Ett exempel finns i portningen av metoden EnableClipboardContentChangedNotifications.

Du kan också registrera en händelsehanterare i XAML-markering.

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

I C# kan din OpenButton_Click-metod vara privat, och XAML kan fortfarande ansluta den till ButtonBase.Click-händelsen som skapats av OpenButton.

I C++/WinRT måste din OpenButton_Click-metod vara offentlig i din implementeringstypom du vill registrera den i XAML-markering. Om du registrerar en händelsehanterare endast i imperativ kod behöver händelsehanteraren inte vara offentlig.

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

Du kan också göra XAML-sidan som utför registreringen till vän med din implementeringstyp och göra OpenButton_Click privat.

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);
    }
};

Ett sista scenario är när C#-projektet som du porterar binder till händelsehanteraren i uppmärkningen (mer information om det scenariot finns i Functions in x:Bind).

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

Du kan bara ändra den markeringen till den enklare Click="OpenButton_Click". Eller, om du föredrar det, kan du behålla den markeringen som den är. Allt du behöver göra för att stödja det är att deklarera händelsehanteraren i IDL.

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

Note

Deklarera funktionen som void även om du implementerar den som Eld och glöm.

Ändringar som omfattar språksyntaxen

Category C# C++/WinRT Se även
Åtkomstmodifierare public \<member\> public:
    \<member\>
Portering av Button_Click-metoden
Få åtkomst till en datamedlem this.variable this->variable  
Asynkron åtgärd async Task ... IAsyncAction ... IAsyncAction-gränssnitt, samtidighet och asynkrona åtgärder med C++/WinRT
Asynkron åtgärd async Task<T> ... IAsyncOperation<T> ... IAsyncOperation-gränssnitt, samtidighet och asynkrona åtgärder med C++/WinRT
Fire-and-forget-metoden (innebär asynkronisering) async void ... winrt::fire_and_forget ... Portering av metoden CopyButton_Click, Starta och glöm
Få åtkomst till en uppräknad konstant E.Value E::Value Porta metoden DisplayChangedFormats
Kooperativt vänta await ... co_await ... Porta CopyButton_Click-metoden
Samling av projekterade typer i ett privat fält private List<MyRuntimeClass> myRuntimeClasses = new List<MyRuntimeClass>(); std::vector
<MyNamespace::MyRuntimeClass>
m_myRuntimeClasses;
GUID-konstruktion 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} };
Namnområdesavgränsare A.B.T A::B::T
Null null nullptr Porta metoden UpdateStatus
Hämta ett typobjekt typeof(MyType) winrt::xaml_typename<MyType>() Porta egenskapen Scenarios
Parameterdeklaration för en metod MyType MyType const& Parameteröverföring
Parameterdeklaration för en asynkron metod MyType MyType Parameteröverföring
Anropa en statisk metod T.Method() T::Method()
Strängar string, eller System.String winrt::hstring Stränghantering i C++/WinRT
Strängliteral "a string literal" L"a string literal" Portning av konstruktorn, Nuvarande och FEATURE_NAME
Infererad (eller härledd) typ var auto Portering av metoden BuildClipboardFormatsOutputString
Användningsdirektiv using A.B.C; using namespace A::B::C; Portering av konstruktorn, Aktuell och FEATURE_NAME
Verbatim/rå strängliteral @"verbatim string literal" LR"(raw string literal)" Porta metoden DisplayToast

Note

Om en rubrikfil inte innehåller ett using namespace direktiv för ett givet namnområde måste du fullständigt kvalificera alla typnamn för det namnområdet, eller åtminstone kvalificera dem tillräckligt för att kompilatorn ska kunna hitta dem. Ett exempel finns i Portering av metoden DisplayToast.

Portering av klasser och medlemmar

För varje C#-typ måste du bestämma om du vill portera den till en Windows Runtime typ eller till en vanlig C++-klass/struct/uppräkning. Mer information och detaljerade exempel som visar hur dessa beslut kan fattas finns i Porting the Clipboard sample to C++/WinRT from C#.

En C#-egenskap blir vanligtvis en accessorfunktion, en mutatorfunktion och en stöddatamedlem. Mer information och ett exempel finns i Migrera egenskapen IsClipboardContentChangedEnabled.

För icke-statiska fält gör du dem till datamedlemmar av din implementeringstyp.

Ett statiskt fält i C# blir en statisk åtkomst- och/eller mutatorfunktion i C++/WinRT. Mer information och ett exempel finns i Portering av konstruktorn, Current och FEATURE_NAME.

För medlemsfunktioner måste du återigen bestämma för var och en om den tillhör IDL eller inte, eller om det är en offentlig eller privat medlemsfunktion av din implementeringstyp. Mer information och exempel på hur du bestämmer finns i IDL för MainPage-typen.

Portera XAML-kod och resursfiler

När det gäller att portera Urklippsexemplet till C++/WinRT från C#, kunde vi använda samma XAML-markering (inklusive resurser) och tillgångsfiler i C# och C++/WinRT-projektet. I vissa fall är redigeringar av markering nödvändiga för att uppnå detta. Se Kopiera XAML och de format som krävs för att slutföra porteringen av MainPage.

Ändringar som omfattar procedurer på språket

Category C# C++/WinRT Se även
Livslängdshantering i en asynkron metod N/A auto lifetime{ get_strong() }; eller
auto lifetime = get_strong();
Porta CopyButton_Click-metoden
Avyttrande using (var t = v) auto t{ v };
t.Close(); // or let wrapper destructor do the work
Portering av metoden CopyImage
Skapa objekt new MyType(args) MyType{ args } eller
MyType(args)
Porta egenskapen Scenarios
Skapa en icke-initierad referens MyType myObject; MyType myObject{ nullptr }; eller
MyType myObject = nullptr;
Portering av konstruktorn, Aktuell och FEATURE_NAME
Skapa objekt i variabel med args var myObject = new MyType(args); auto myObject{ MyType{ args } }; eller
auto myObject{ MyType(args) }; eller
auto myObject = MyType{ args }; eller
auto myObject = MyType(args); eller
MyType myObject{ args }; eller
MyType myObject(args);
Portering av metoden Footer_Click
Konstruera objekt till variabel utan args var myObject = new T(); MyType myObject; Portering av metoden BuildClipboardFormatsOutputString
Kortform för objektinitiering var p = new FileOpenPicker{
    ViewMode = PickerViewMode.List
};
FileOpenPicker p;
p.ViewMode(PickerViewMode::List);
Massvektoråtgärd var p = new FileOpenPicker{
    FileTypeFilter = { ".png", ".jpg", ".gif" }
};
FileOpenPicker p;
p.FileTypeFilter().ReplaceAll({ L".png", L".jpg", L".gif" });
Porta CopyButton_Click-metoden
Iterera över samlingar foreach (var v in c) for (auto&& v : c) Portering av metoden BuildClipboardFormatsOutputString
Fånga ett undantag catch (Exception ex) catch (winrt::hresult_error const& ex) Portering av metoden PasteButton_Click
Information om undantaget ex.Message ex.message() Porta metoden PasteButton_Click
Hämta ett egenskapsvärde myObject.MyProperty myObject.MyProperty() Portering av metoden NotifyUser
Ange ett egenskapsvärde myObject.MyProperty = value; myObject.MyProperty(value);
Öka egenskapsvärdet myObject.MyProperty += v; myObject.MyProperty(thing.Property() + v);
För strängar, använd en strängbyggare
ToString() myObject.ToString() winrt::to_hstring(myObject) ToString()
Språksträng till Windows Runtime-sträng N/A winrt::hstring{ s }
Strängkonstruktion StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Strängkonstruktion
Stränginterpolation $"{i++}) {s.Title}" winrt::to_hstring och/eller winrt::hstring::operator+ Portering av metoden OnNavigatedTo
Tom sträng för jämförelse System.String.Empty winrt::hstring::empty Porta metoden UpdateStatus
Skapa tom sträng var myEmptyString = String.Empty; winrt::hstring myEmptyString{ L"" };
Ordlisteåtgärder 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)
Typkonvertering (inträffar vid fel) (MyType)v v.as<MyType>() Portering av metoden Footer_Click
Typomvandling (null vid misslyckande) v as MyType v.try_as<MyType>() Portering av PasteButton_Click-metoden
XAML-element med x:Name är egenskaper MyNamedElement MyNamedElement() Portering av konstruktorn, Aktuell och FEATURE_NAME
Växla till användargränssnittstråden CoreDispatcher.RunAsync DispatcherQueue.TryEnqueue eller winrt::resume_foreground Portera metoden NotifyUser och porta metoden HistoryAndRoaming
UI-elementkonstruktion i imperativ kod på en XAML-sida Se UI-elementkonstruktion Se UI-elementkonstruktion

Följande avsnitt går in närmare på några av objekten i tabellen.

UI-elementkonstruktion

Dessa kodexempel visar konstruktionen av ett gränssnittselement i den imperativa koden på en XAML-sida.

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()

C#-typerna innehåller metoden Object.ToString .

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

C++/WinRT tillhandahåller inte den här funktionen direkt, men du kan vända dig till alternativ.

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

C++/WinRT stöder också winrt::to_hstring för ett begränsat antal typer. Du måste lägga till överlagrade funktioner för alla de ytterligare typer som du vill konvertera till sträng.

Language Konvertera int till sträng Konvertera enum till sträng
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);

Om du vill konvertera en enum till en sträng måste du tillhandahålla en implementering av 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));
        }
    }
}

Dessa strängrepresentationer används ofta implicit vid databindning.

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

Dessa bindningar kommer att använda winrt::to_hstring på den bundna egenskapen. När det gäller det andra exemplet ( StatusEnum) måste du ange din egen överbelastning av winrt::to_hstring, annars får du ett kompilatorfel.

Se även Portering av metoden Footer_Click.

Strängbygge

För strängbyggnad har C# en inbyggd StringBuilder-typ .

Category C# C++/WinRT
Strängkonstruktion StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Lägg till en Windows Runtime sträng och bevara null-värden builder.Append(s); builder << std::wstring_view{ s };
Lägga till en ny rad builder.Append(Environment.NewLine); builder << std::endl;
Få åtkomst till resultatet s = builder.ToString(); ws = builder.str();

Se även Portering av metoden BuildClipboardFormatsOutputString och portering av metoden DisplayChangedFormats.

Köra kod i huvudgränssnittstråden

Det här exemplet är hämtat från streckkodsskannerexemplet.

När du vill arbeta med huvudgränssnittstråden i ett C#-projekt använder du vanligtvis metoden DispatcherQueue.TryEnqueue (eller den äldre CoreDispatcher.RunAsync i UWP). Så här ser mönstret ut i C#.

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

Det är mycket enklare att uttrycka det i C++/WinRT. Observera att vi accepterar parametrar efter värde enligt antagandet att vi vill komma åt dem efter den första avstängningspunkten ( co_awaiti det här fallet). Mer information finns i Parameter-passing.

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

Om du behöver utföra arbetet med en annan prioritet än standard kan du se funktionen winrt::resume_foreground , som har en överbelastning som prioriteras. Kodexempel som visar hur du väntar på ett anrop till winrt::resume_foreground finns i Programmering med trådtillhörighet i åtanke.

Definiera dina körningsklasser i IDL

Se IDL för MainPage-typen och Konsolidera dina .idl filer.

Inkludera de C++/WinRT-Windows namnområdeshuvudfiler som du behöver

När du vill använda en typ från en Windows namnområden i C++/WinRT måste du inkludera motsvarande C++/WinRT-Windows namnområdeshuvudfil. Ett exempel finns i Portering av metoden NotifyUser.

Boxning och avboxning

C# boxar automatiskt skalärer till objekt. C++/WinRT kräver att du anropar funktionen winrt::box_value explicit. Båda språken kräver att du packar upp explicit. Se Boxning och avboxning med C++/WinRT.

I tabellerna som följer använder vi dessa definitioner.

C# C++/WinRT
int i; int i;
string s; winrt::hstring s;
object o; IInspectable o;
Operation C# C++/WinRT
Boxning o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
Öppna förpackningen i = (int)o;
s = (string)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

C++/CX och C# utlöser undantag om du försöker avboxa en null-pekare till en värdetyp. C++/WinRT anser att detta är ett programmeringsfel och kraschar. I C++/WinRT använder du funktionen winrt::unbox_value_or om du vill hantera fallet där objektet inte är av den typ som du trodde att det var.

Scenario C# C++/WinRT
Avboxa ett känt heltal i = (int)o; i = unbox_value<int>(o);
Om o är null System.NullReferenceException Krasch
Om o inte är en boxad int System.InvalidCastException Krasch
Avboxa int, använd reservvärde om null; krascha om det är något annat i = o != null ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Avboxa int om möjligt; använd reservlösning för allt annat i = as int? ?? fallback; i = unbox_value_or<int>(o, fallback);

Ett exempel finns i Portera metoden OnNavigatedTo och Portera metoden Footer_Click.

Boxning och avboxning av en sträng

En sträng är på sätt och vis en värdetyp och på andra sätt en referenstyp. C# och C++/WinRT behandlar strängar på olika sätt.

ABI-typen HSTRING är en pekare till en referensberäkningssträng. Men det härleds inte från IInspectable, så det är inte tekniskt ett objekt. Dessutom representerar en null HSTRING den tomma strängen. Boxning av saker som inte härleds från IInspectable görs genom att omsluta dem i en IReference<T>, och Windows Runtime tillhandahåller en standardimplementering i form av PropertyValue-objektet (anpassade typer rapporteras som PropertyType::OtherType).

C# representerar en Windows Runtime sträng som referenstyp, medan C++/WinRT projicerar en sträng som en värdetyp. Det innebär att en rutad null-sträng kan ha olika representationer beroende på hur du kom dit.

Behavior C# C++/WinRT
Deklarationer object o;
string s;
IInspectable o;
hstring s;
Kategori för strängtyp Referenstyp Värdetyp
null HSTRING-projekt som "" hstring{}
Är null och "" identiska? No Yes
Giltighet för null s = null;
s.Length utlöser NullReferenceException
s = hstring{};
s.size() == 0 (giltigt)
Om du tilldelar null-sträng till objekt o = (string)null;
o == null
o = box_value(hstring{});
o != nullptr
Om du tilldelar "" objektet o = "";
o != null
o = box_value(hstring{L""});
o != nullptr

Grundläggande boxning och avboxning.

Operation C# C++/WinRT
Boxa en sträng o = s;
Tom sträng blir ett icke-null-objekt.
o = box_value(s);
Tom sträng blir ett icke-null-objekt.
Packa upp en känd sträng s = (string)o;
Null-objektet blir null-sträng.
InvalidCastException om det inte är en sträng.
s = unbox_value<hstring>(o);
Null-objekt kraschar.
Kraschar om det inte är en sträng.
Packa upp en möjlig sträng s = o as string;
Null-objekt eller icke-sträng blir nullsträng.

OR

s = o as string ?? fallback;
Null eller ett värde som inte är en sträng ger ett återgångsvärde.
Tom sträng bevarad.
s = unbox_value_or<hstring>(o, fallback);
Null eller ett värde som inte är en sträng ger ett återgångsvärde.
Tom sträng bevarad.

Göra en klass tillgänglig för {Binding}-markeringstillägget

Om du tänker använda markeringstillägget {Binding} till databindning till din datatyp kan du läsa Bindningsobjekt som deklarerats med {Binding}.

Använda objekt från XAML-markering

I ett C#-projekt kan du få åtkomst till privata medlemmar och namngivna element från XAML-kod. Men i C++/WinRT måste alla entiteter som används med XAML {x:Bind}-markeringstillägget exponeras offentligt i IDL.

Dessutom visas true eller false i C# vid bindning till ett booleskt värde, men i C++/WinRT visas Windows.Foundation.IReference`1<Boolean>.

Mer information och kodexempel finns i Använda objekt från markup.

Göra en datakälla tillgänglig för XAML-markering

I C++/WinRT version 2.0.190530.8 eller senare skapar winrt::single_threaded_observable_vector en observerbar vektor som stöder både IObservableVector<T> och IObservableVector<IInspectable>. Ett exempel finns i Portering av egenskapen Scenarios.

Du kan skriva midl-filen (.idl) så här (se även Factoring runtime-klasser till Midl-filer (.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; };
    }
}

Och implementera så här.

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

Mer information finns i XAML-objektkontroller, bind till en C++/WinRT-samling och samlingar med C++/WinRT.

Göra en datakälla tillgänglig för XAML-markering (före C++/WinRT 2.0.190530.8)

XAML-databindning kräver att en objektkälla implementerar IIterable<IInspectable>, samt en av följande kombinationer av gränssnitt.

  • IObservableVector<IInspectable>
  • IBindableVector och INotifyCollectionChanged
  • IBindableVector och IBindableObservableVector
  • IBindableVector själv (svarar inte på ändringar)
  • IVector<IInspectable>
  • IBindableIterable (itererar och sparar element i en privat samling)

Ett generiskt gränssnitt som IVector<T> kan inte identifieras under körning. Varje IVector<T> har en annan gränssnittsidentifierare (IID), som är en funktion av T. Alla utvecklare kan utöka uppsättningen T godtyckligt, så det är tydligt att XAML-bindningskoden aldrig kan känna till den fullständiga uppsättningen att fråga efter. Den begränsningen är inte ett problem för C# eftersom varje CLR-objekt som implementerar IEnumerable<T> implementerar IEnumerable automatiskt. På ABI-nivå innebär det att varje objekt som implementerar IObservableVector<T> automatiskt implementerar IObservableVector<IInspectable>.

C++/WinRT erbjuder inte den garantin. Om en C++/WinRT-körningsklass implementerar IObservableVector<T> kan vi inte anta att en implementering av IObservableVector<IInspectable> på något sätt också tillhandahålls.

Därför måste det föregående exemplet se ut så här.

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

Och implementeringen.

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

Om du behöver komma åt objekt i m_bookSkus måste du skicka tillbaka dem till Bokhandel::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;
}

Härledda klasser

För att kunna ärva från en runtimeklass måste basklassen vara komponerbar. C# kräver inte att du vidtar några särskilda åtgärder för att göra dina klasser komposterbara, men det gör C++/WinRT. Du använder det oförseglade nyckelordet för att ange att du vill att din klass ska kunna användas som basklass.

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

I huvudfilen för implementeringstypen måste du inkludera huvudfilen för basklassen innan du tar med den automatiskt genererade rubriken för den härledda klassen. Annars får du fel som "Olaglig användning av den här typen som ett uttryck".

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

Viktiga API:er