Dela via


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

Tips/Råd

Om du har läst det här avsnittet tidigare och återvänder till det med en viss uppgift i åtanke kan du hoppa till 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 UWP-appexemplen (Universal Windows Platform) finns i det tillhörande avsnittet Porting the Clipboard sample to C++/WinRT from C#. Du kan få portningspraxis och erfarenhet genom att följa med i den genomgången och portera exemplet själv samtidigt.

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

Fallstudien Att portera Urklippsexemplet till C++/WinRT från 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.

  • Portera 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>. I C# beräknas även vissa Windows Runtime-åtgärder som praktiska C#-språkfunktioner. 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).
  • portspråksyntax. 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 (::).
  • portspråksprocedur. 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 omfattar 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 hanteras implicit av C#, i bakgrunden. Dessa detaljer utförs explicit i C++/WinRT. Ett exempel är att du använder en .idl-fil för att definiera dina körningstidklasser.

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

Aktivitet Innehåll
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 körningsklass i en Windows Runtime-komponent.
Portera en asynkron metod Det är en bra idé att den första raden i en asynkron metod i en C++/WinRT-körningsklass är auto lifetime = get_strong(); (se Säker åtkomst till den här pekaren i en coroutine för klassmedlemmar).

Portning från Task, se asynkron åtgärd.
Portning från Task<T>, se Async-åtgärd.
Portning från async void, se även fire-and-forget-metoden.
Portera en klass Först, avgör om klassen måste vara en runtime-klass eller om den kan vara en vanlig klass. För att hjälpa dig att bestämma dig, se den allra första början av Författar-API:er 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-komponenteller Om du redigerar en körningsklass som ska refereras i XAML-användargränssnittet.

Dessa länkar beskriver detta mer detaljerat, men en runtime-klass måste deklareras i IDL. Om projektet 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 Solution Explorer, 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.
Överför en vanlig klass Se Om du inte redigera en körningsklass.
Författare IDL Introduktion till Microsoft Interface Definition Language 3.0
Om du skapar en körningsklass för att användas i ditt XAML-användargränssnitt
Använda objekt från XAML-
Definiera dina körningsklasser i IDL -
Porta en samling samlingar med C++/WinRT
Göra en datakälla tillgänglig för XAML-markering
associativ behållare
Vector-medlemsåtkomst
Överföra en händelse Händelsehanterardelegat som klassmedlem
Återkalla händelsehanteringsdelegat
Portera en metod Från C#: private async void SampleButton_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) { ... }
Till filen C++/WinRT .h: fire_and_forget SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&);
Till filen C++/WinRT .cpp: fire_and_forget OcrFileImage::SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&) {...}
Portsträngar Stränghantering i C++/WinRT
ToString
Strängbyggnad
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)
Kastar ett undantag om avboxing misslyckas. Se även avsnittet om boxning och uppackning.

C#: o as Value? ?? fallback
C++/WinRT: unbox_value_or<Value>(o, fallback)
Returnerar reservvärde om avboxning misslyckas. Se även avsnittet om boxning och uppackning.

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

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

Ändringar som omfattar språkprojektionen

Kategori C# C++/WinRT Se även
Otyperat objekt objecteller System.Object Windows::Foundation::IInspectable att porta 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, kan du använda 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 }); att porta metoden EnableClipboardContentChangedNotifications
Återkalla händelsehanterardelegat myObject.EventName -= Handler; myObject.EventName(token); att porta 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 en händelsehanterardelegat enligt beskrivningen i Hantera händelser med hjälp av ombud i C++/WinRT. Se även Portering av EnableClipboardContentChangedNotifications-metoden.

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 Portera 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 utlöses av OpenButton.

I C++/WinRT måste din OpenButton_Click-metod vara offentlig i din -implementeringstypom du vill registrera den i XAML-. 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::Windows::UI::Xaml::RoutedEventArgs const& args);
    }
};

Du kan också göra registreringen av XAML-sidan till en vän av din implementeringstyp och OpenButton_Click privat.

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

Ett sista scenario är när C#-projektet som du porterar binder till händelsehanteraren från markup (för mer bakgrund om det scenariot, se Funktioner i 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 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, Windows.UI.Xaml.RoutedEventArgs e);

Anmärkning

Deklarera funktionen som void även om du implementera den som Fire och glöm.

Ändringar som omfattar språksyntaxen

Kategori C# C++/WinRT Se även
Åtkomstmodifierare public \<member\> public:
    \<member\>
Portera Button_Click-metoden
Få åtkomst till en datamedlem this.variable this->variable  
Asynkron åtgärd async Task ... IAsyncAction ... IAsyncAction-gränssnittet, Samtidighet och asynkrona åtgärder med C++/WinRT-
Async-åtgärd async Task<T> ... IAsyncOperation<T> ... IAsyncOperation-gränssnittet, Samtidighet och asynkrona åtgärder med C++/WinRT-
Fire-and-forget-metoden (innebär asynkronisering) async void ... winrt::fire_and_forget ... Portera CopyButton_Click metoden, Skjut och glöm
Få åtkomst till en uppräknad konstant E.Value E::Value Portering DisplayChangedFormats-metod
Kooperativt vänta await ... co_await ... Portera metoden CopyButton_Click
Samling av projekterade typer som 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
Noll null nullptr porta metoden UpdateStatus
Hämta ett typobjekt typeof(MyType) winrt::xaml_typename<MyType>() Portering av Scenarier egenskap
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" överföra konstruktören, Aktuelloch FEATURE_NAME
Uppskjuten (eller härledd) typ var auto Portering av metoden BuildClipboardFormatsOutputString
Användningsdirektiv using A.B.C; using namespace A::B::C; överföra konstruktören, Aktuelloch FEATURE_NAME
Ordagrann eller rå strängliteral @"verbatim string literal" LR"(raw string literal)" porta metoden DisplayToast

Anmärkning

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 Porting the DisplayToast method.

Portningsklasser 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 illustrerar hur du fattar dessa beslut 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 Porting the IsClipboardContentChangedEnabled property.

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

Ett statiskt C#-fält blir en statisk C++ / WinRT-åtkomstfunktion och/eller mutatorfunktion. Mer information och ett exempel finns i Porting the constructor, Currentoch 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 man bestämmer finns i IDL för MainPage typ.

Porta XAML markup-kod och resursfiler

När det gäller att överföra Urklippsexemplet från C# till C++/WinRTkunde vi använda samma XAML-markup (inklusive resurser) och resursfiler i både 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 portningen MainPage-.

Ändringar som omfattar procedurer på språket

Kategori C# C++/WinRT Se även
Livslängdshantering i en asynkron metod Inte tillgänglig auto lifetime{ get_strong() }; eller
auto lifetime = get_strong();
Portera metoden CopyButton_Click
Avfallshantering using (var t = v) auto t{ v };
t.Close(); // or let wrapper destructor do the work
Portera metoden CopyImage
Skapa objekt new MyType(args) MyType{ args } eller
MyType(args)
Portering av Scenarier egenskap
Skapa en icke-initierad referens MyType myObject; MyType myObject{ nullptr }; eller
MyType myObject = nullptr;
överföra konstruktören, Aktuelloch 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);
portera Footer_Click-metoden
Konstruera objekt till variabel utan args var myObject = new T(); MyType myObject; Portering av metoden BuildClipboardFormatsOutputString
Kortare syntax 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" });
Portera metoden CopyButton_Click
Iterera över samling 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) portera metoden PasteButton_Click
Undantagsinformation ex.Message ex.message() portera metoden PasteButton_Click
Hämta ett egenskapsvärde myObject.MyProperty myObject.MyProperty() Portera NotifyUser metoden
Ange ett egenskapsvärde myObject.MyProperty = value; myObject.MyProperty(value);
Öka ett egenskapsvärde myObject.MyProperty += v; myObject.MyProperty(thing.Property() + v);
För strängar, växla till en strängbyggare
ToString() myObject.ToString() winrt::to_hstring(myObject) ToString()
Omvandling av språksträng till Windows Runtime-sträng Inte tillgänglig winrt::hstring{ s }
Strängkonstruering StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Strängbyggnad
Stränginterpolation $"{i++}) {s.Title}" winrt::to_hstring, och/eller winrt::hstring::operator+ Porting the OnNavigatedTo metod
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 (kasta vid misslyckande) (MyType)v v.as<MyType>() portera Footer_Click-metoden
Typkonvertering (nullvärde vid misslyckande) v as MyType v.try_as<MyType>() portera metoden PasteButton_Click
XAML-element med x:Name är egenskaper MyNamedElement MyNamedElement() överföra konstruktören, Aktuelloch FEATURE_NAME
Växla till användargränssnittstråden CoreDispatcher.RunAsync CoreDispatcher.RunAsynceller winrt::resume_foreground Porting the NotifyUser methodoch Porting the HistoryAndRoaming method
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 = (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()

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 överlagringar för alla ytterligare typer som du vill konvertera till strängar.

Språk Stringify int (konvertera ett heltal till en sträng) Stringify-uppräkning
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);

När det gäller att strängifiera en enum måste du ange implementeringen 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ängifieringar konsumeras ofta implicit av 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 utföra winrt::to_hstring för den bundna egenskapen. När det gäller det andra exemplet (StatusEnum) måste du ange en egen överlagring av winrt::to_hstring, annars får du ett kompilatorfel.

Se även Porting the Footer_Click method.

Strängkonstruering

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

Kategori C# C++/WinRT
Strängkonstruering StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Lägg till en Windows Runtime-sträng som bevarar 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 Portera BuildClipboardFormatsOutputString-metodenoch Portera DisplayChangedFormats-metoden.

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 CoreDispatcher.RunAsync, så här.

private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        // 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_await, i det här fallet). Mer information finns i parameteröverföring.

winrt::fire_and_forget Watcher_Added(DeviceWatcher sender, winrt::DeviceInformation args)
{
    co_await Dispatcher();
    // 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_foregroundfinns i Programmering med trådtillhörighet i åtanke.

Definiera dina körningsklasser i IDL

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

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

När du vill använda en typ från en Windows-namnrymd i C++/WinRT måste du inkludera motsvarande C++/WinRT Windows-namnområdeshuvudfil. Ett exempel finns i Porting the NotifyUser method.

Boxning och avboxning

C# omvandlar automatiskt skalärtyper 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;
Verksamhet 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# genererar undantag om du försöker ta bort 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 det fall där objektet inte är av den typ som du trodde att det var.

Scenarium C# C++/WinRT
Ta bort ett känt heltal i = (int)o; i = unbox_value<int>(o);
Om o är null System.NullReferenceException Krasch
Om o inte är en inkapslad int System.InvalidCastException Krasch
Avboxa int, använd övergångslösning om null; krascha om något annat i = o != null ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Avboxa int om möjligt; använd reservmetod för annat i = as int? ?? fallback; i = unbox_value_or<int>(o, fallback);

Ett exempel finns i Porting the OnNavigatedTo method, and Porting the Footer_Click method.

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 sett ett objekt. Dessutom representerar en null-HSTRING- den tomma strängen. Inkapsling av objekt som inte härrör från IInspectable utförs genom att omsluta dem i ett IReference<T>. 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 en 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.

Beteende 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? Nej Ja
Giltighet för null s = null;
s.Length genererar 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 "" till objekt o = "";
o != null
o = box_value(hstring{L""});
o != nullptr

Grundläggande boxning och avboxning.

Verksamhet C# C++/WinRT
Placera en sträng i en låda 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 den inte är en sträng.
s = unbox_value<hstring>(o);
Nullobjektet kraschar.
Kör i kras 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.

ELLER

s = o as string ?? fallback;
Null eller icke-sträng blir reserv.
Tom sträng bevarad.
s = unbox_value_or<hstring>(o, fallback);
Null eller icke-sträng blir reserv.
Tom sträng bevarad.

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

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

Konsumera objekt från XAML-markup

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

Bindning till ett booleskt värde visar också true eller false i C#, men det visar Windows.Foundation.IReference`1<Boolean> i C++/WinRT.

För mer information och kodexempel, se Konsumera 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 Porting the Scenarios property.

Du kan skapa Midl-fil (.idl) så här (se även Factoring-körningsklasser i Midl-filer (.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; };
    }
}

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, koppla till en C++/WinRT-samlingoch 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 vid körning. Varje IVector<T> har en annan gränssnittsidentifierare (IID), som är en funktion av T. Alla utvecklare kan utöka uppsättningen med 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 automatiskt IEnumerable. På ABI-nivå innebär det att varje objekt som implementerar IObservableVector<T> implementerar automatiskt 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_bookSkusmåste du anropa QI för att konvertera dem tillbaka till 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;
}

Härledda klasser

För att kunna härledas från en körningsklass 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 nyckelordet oförseglat för att ange att du vill att klassen ska kunna användas som basklass.

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

I huvudfilen för din implementeringstypmåste du inkludera huvudfilen för basklassen innan du inkluderar 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