Kommentar
Å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.
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.MyPropertytillmyObject.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)oC++/WinRT: unbox_value<Value>(o)Genererar ett undantag om avboxningen misslyckas. Se även Boxning och avboxning. C#: o as Value? ?? fallbackC++/WinRT: unbox_value_or<Value>(o, fallback)Returnerar reservvärdet om uppackningen misslyckas. Se även Boxning och avboxning. C#: (Class)oC++/WinRT: o.as<Class>()Genererar om konverteringen misslyckas. C#: o as ClassC++/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() }; ellerauto 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 } ellerMyType(args) |
Porta egenskapen Scenarios |
| Skapa en icke-initierad referens | MyType myObject; |
MyType myObject{ nullptr }; ellerMyType 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 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 (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.
Portningsrelaterade uppgifter som är specifika för C++/WinRT
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
Relaterade ämnen
Windows developer