Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
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.MyPropertytillmyObject.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)oC++/WinRT: unbox_value<Value>(o)Kastar ett undantag om avboxing misslyckas. Se även avsnittet om boxning och uppackning. C#: o as Value? ?? fallbackC++/WinRT: unbox_value_or<Value>(o, fallback)Returnerar reservvärde om avboxning misslyckas. Se även avsnittet om boxning och uppackning. C#: (Class)oC++/WinRT: o.as<Class>()Kastar om konverteringen misslyckas. C#: o as ClassC++/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() }; ellerauto 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 } ellerMyType(args) |
Portering av Scenarier egenskap |
| Skapa en icke-initierad referens | MyType myObject; |
MyType myObject{ nullptr }; ellerMyType 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 existingv = map[k]; // throws if not presentmap.ContainsKey(k) |
map.Insert(k, v); // replaces any existingv = map.Lookup(k); // throws if not presentmap.HasKey(k) |
|
| 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.
Portningsrelaterade uppgifter som är specifika för C++/WinRT
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>
{
...
}
}