Passare a C++/WinRT da C#

Suggerimento

Se hai letto questo argomento in precedenza e stai tornando a questo argomento tenendo presente un'attività specifica, puoi passare alla sezione Trova contenuto in base all'attività che stai eseguendo in questo argomento.

Questo argomento cataloga in modo completo i dettagli tecnici relativi alla conversione del codice sorgente in un progetto C# al relativo equivalente in C++/WinRT.

Per un case study sulla conversione di uno degli esempi di app Piattaforma UWP (Universal Windows Platform), vedi l'argomento complementare Conversione dell'esempio degli Appunti in C++/WinRT da C#. È possibile acquisire esperienza e pratica di conversione seguendo questa procedura dettagliata e la conversione dell'esempio manualmente man mano che si procede.

Come prepararsi e cosa aspettarsi

Il case study Porting the Clipboard sample to C++/WinRT from C# (Conversione dell'esempio degli Appunti in C++/WinRT da C#) illustra esempi dei tipi di decisioni di progettazione software che verranno prese durante la conversione di un progetto in C++/WinRT. È quindi consigliabile prepararsi per la conversione ottenendo una conoscenza approfondita del funzionamento del codice esistente. In questo modo, si otterrà una buona panoramica delle funzionalità dell'app e della struttura del codice, quindi le decisioni che si prendono verranno sempre prese in avanti e nella giusta direzione.

In termini di quali tipi di modifiche di conversione si prevede, è possibile raggrupparli in quattro categorie.

  • Trasferire la proiezione del linguaggio. Il Windows Runtime (WinRT) viene proiettato in vari linguaggi di programmazione. Ciascuna di queste proiezioni linguistiche è concepita per risultare idiomatica nel linguaggio di programmazione in questione. Per C#, alcuni tipi di Windows Runtime vengono proiettati come tipi di .NET. Ad esempio, si eseguirà la conversione di System.Collections.Generic.IReadOnlyList<T> in Windows. Foundation.Collections.IVectorView<T>. Anche in C#, alcune operazioni di Windows Runtime vengono proiettate come funzionalità del linguaggio C# utili. Un esempio è che in C# si usa la sintassi dell'operatore += per registrare un delegato di gestione degli eventi. Si tradurranno quindi le funzionalità del linguaggio, ad esempio, nell'operazione fondamentale da eseguire (registrazione eventi, in questo esempio).
  • Sintassi del linguaggio delle porte. Molte di queste modifiche sono semplici trasformazioni meccaniche, sostituendo un simbolo per un altro. Ad esempio, la modifica del punto (.) in due punti (::).
  • Procedura del linguaggio di porta. Alcuni di questi possono essere semplici e ripetitivi (ad esempio myObject.MyProperty in myObject.MyProperty()). Altri richiedono modifiche più approfondite, ad esempio la conversione di una routine che prevede l'uso di System.Text.StringBuilder a uno che implica l'uso di std::wostringstream.
  • Attività relative al porting specifiche di C++/WinRT. Alcuni dettagli del Windows Runtime vengono presi in considerazione in modo implicito da C#, dietro le quinte. Questi dettagli vengono eseguiti in modo esplicito in C++/WinRT. Un esempio è che si usa un .idl file per definire le classi di runtime.

Dopo l'indice basato su attività che segue, le altre sezioni di questo argomento sono strutturate in base alla tassonomia precedente.

Trovare contenuto in base all'attività che si sta eseguendo

Attività Content
Creare un componente Windows Runtime (WRC) Alcune funzionalità possono essere ottenute (o determinate API chiamate) solo con C++. È possibile considerare tale funzionalità in un WRC C++/WinRT e quindi usare WRC da (ad esempio) un'app C#. Vedi componenti di Windows Runtime con C++/WinRT e Se stai scrivendo una classe runtime in un componente di Windows Runtime.
Eseguire il porting di un metodo asincrono È consigliabile che la prima riga di un metodo asincrono in una classe runtime C++/WinRT sia auto lifetime = get_strong(); (vedere Accesso sicuro al puntatore this in una coroutine membro di una classe).

Conversione da Task, vedere Azione asincrona.
Migrazione da Task<T>, vedi Operazione asincrona.
Conversione da async void, vedere Metodo Fire-and-forget.
Convertire una classe Determinare prima di tutto se la classe deve essere una classe di runtime o se può essere una classe normale. Per aiutarti a decidere, vedi l'inizio di Creare API con C++/WinRT. Vedere quindi le tre righe seguenti.
Convertire una classe di runtime Classe che condivide funzionalità all'esterno dell'app C++ o una classe usata nel data binding XAML. Vedi Se stai creando una classe di runtime in un componente Windows Runtime o Se stai creando una classe di runtime a cui fare riferimento nell'interfaccia utente XAML.

Questi collegamenti descrivono questo argomento in modo più dettagliato, ma una classe di runtime deve essere dichiarata in IDL. Se il project contiene già un file IDL ,ad esempio Project.idl, è consigliabile dichiarare qualsiasi nuova classe di runtime in tale file. In IDL dichiara tutti i metodi e i membri dati che verranno usati all'esterno dell'app o che verranno usati in XAML. Dopo aver aggiornato il file IDL, ricompila ed esamina i file stub generati (.h e .cpp) nella cartella Generated Files del progetto (in Esplora soluzioni, con il nodo del progetto selezionato, assicurati che Mostra tutti i file sia attivato). Confrontare i file stub con i file già presenti nel progetto, aggiungere file o aggiungere/aggiornare firme di funzione in base alle esigenze. La sintassi del file Stub è sempre corretta, quindi è consigliabile usarla per ridurre al minimo gli errori di compilazione. Quando gli stub nel progetto corrispondono a quelli nei file stub, è possibile procedere e implementarli eseguendo la conversione del codice C#.
Eseguire il porting di una classe ordinaria Vedi Se non stai creando una classe di runtime.
IDL autore Introduzione a Microsoft Interface Definition Language 3.0
Se stai creando una classe di runtime a cui fare riferimento nell'interfaccia utente XAML
Utilizzo di oggetti dal markup XAML
Definire le classi di runtime in IDL
Trasferire una raccolta Collezioni con C++/WinRT
Rendere disponibile un'origine dati per il markup XAML
Contenitore associativo
Accesso ai membri di Vector
Porta un evento Delegato del gestore eventi come membro della classe
Revocare il delegato del gestore eventi
Convertire un metodo Da C#: private async void SampleButton_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e) { ... }
Nel file C++/WinRT .h : fire_and_forget SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&);
Nel file C++/WinRT .cpp : fire_and_forget OcrFileImage::SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&) {...}
Stringhe di porta Gestione delle stringhe in C++/WinRT
ToString
Costruzione di stringhe
Boxing e unboxing di una stringa
Conversione dei tipi (cast dei tipi) C#: o.ToString()
C++/WinRT: to_hstring(static_cast<int>(o))
Vedere anche ToString.

C#: (Value)o
C++/WinRT: unbox_value<Value>(o)
Genera un'eccezione se l'unboxing non riesce. Vedi anche Boxing e unboxing.

C#: o as Value? ?? fallback
C++/WinRT: unbox_value_or<Value>(o, fallback)
Restituisce il fallback se l'unboxing ha esito negativo. Vedi anche Boxing e unboxing.

C#: (Class)o
C++/WinRT: o.as<Class>()
Genera un'eccezione se la conversione non riesce.

C#: o as Class
C++/WinRT: o.try_as<Class>()
Restituisce Null se la conversione non riesce.

Modifiche che coinvolgono la proiezione del linguaggio

Category C# C++/WinRT Vedere anche
Oggetto non tipizzato object, oppure System.Object Windows::Foundation::IInspectable Conversione del metodo EnableClipboardContentChangedNotifications
Spazi dei nomi di proiezione using System; using namespace Windows::Foundation;
using System.Collections.Generic; using namespace Windows::Foundation::Collections;
Dimensioni di una raccolta collection.Count collection.Size() Conversione del metodo BuildClipboardFormatsOutputString
Tipo di raccolta tipico Ilist<T> e Add per aggiungere un elemento. IVector<T> e Append per aggiungere un elemento. Se usi un std::vector in qualsiasi punto del codice, usa push_back per aggiungere un elemento.
Tipo di raccolta di sola lettura IReadOnlyList<T> IVectorView<T> Conversione del metodo BuildClipboardFormatsOutputString
Delegato del gestore eventi come membro della classe myObject.EventName += Handler; token = myObject.EventName({ get_weak(), &Class::Handler }); Conversione del metodo EnableClipboardContentChangedNotifications
Revocare il delegato del gestore eventi myObject.EventName -= Handler; myObject.EventName(token); Conversione del metodo EnableClipboardContentChangedNotifications
Contenitore associativo IDictionary<K, V> IMap<K, V>
Accesso ai membri del vettore x = v[i];
v[i] = x;
x = v.GetAt(i);
v.SetAt(i, x);

Registrare/revocare un gestore eventi

In C++/WinRT sono disponibili diverse opzioni sintattiche per registrare/revocare un delegato del gestore eventi, come descritto in Gestire gli eventi usando delegati in C++/WinRT. Vedere anche Conversione del metodo EnableClipboardContentChangedNotifications.

Talvolta, ad esempio quando il destinatario dell’evento (un oggetto che gestisce un evento) sta per essere distrutto, è opportuno revocare un gestore di eventi in modo che l’origine dell’evento (l’oggetto che genera l’evento) non invochi un oggetto distrutto. Vedere Revocare un delegato registrato. In casi simili, creare una variabile membro event_token per i gestori eventi. Per un esempio, vedere Conversione del metodo EnableClipboardContentChangedNotifications.

Puoi anche registrare un gestore eventi nel markup XAML.

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

In C# il metodo OpenButton_Click può essere privato e XAML sarà comunque in grado di connetterlo all'evento ButtonBase.Click generato da OpenButton.

In C++/WinRT il metodo OpenButton_Click deve essere pubblico nel tipo di implementazionese vuoi registrarlo nel markup XAML. Se si registra un gestore eventi solo nel codice imperativo, il gestore eventi non deve essere pubblico.

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

In alternativa, puoi rendere la pagina XAML di registrazione un tipo friend del tuo tipo di implementazione e rendere privato il metodo OpenButton_Click.

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

Un ultimo scenario si verifica quando il progetto C# che si sta convertendo si associa al gestore eventi nel markup (per ulteriori informazioni su questo scenario, vedere Funzioni in x:Bind).

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

È sufficiente modificare il markup impostando il codice più semplice Click="OpenButton_Click". In alternativa, se si preferisce, è possibile mantenere il markup così come è. Tutto quello che devi fare per supportarlo è dichiarare il gestore eventi in IDL.

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

Note

Dichiara la funzione come void anche se la implementi come fire-and-forget.

Modifiche che coinvolgono la sintassi del linguaggio

Category C# C++/WinRT Vedere anche
Modificatori di accesso public \<member\> public:
    \<member\>
Conversione del metodo Button_Click
Accedere a un membro dati this.variable this->variable  
Azione asincrona async Task ... IAsyncAction ... Interfaccia IAsyncAction, concorrenza e operazioni asincrone con C++/WinRT
Operazione asincrona async Task<T> ... IAsyncOperation<T> ... Interfaccia IAsyncOperation, concorrenza e operazioni asincrone con C++/WinRT
Metodo fire-and-forget (implica asincronicità) async void ... winrt::fire_and_forget ... Adattamento del metodo CopyButton_Click, avvia e dimentica
Accedere a una costante enumerata E.Value E::Value Conversione del metodo DisplayChangedFormats
Attendere in modo cooperativo await ... co_await ... Conversione del metodo CopyButton_Click
Raccolta dei tipi proiettati come campo privato private List<MyRuntimeClass> myRuntimeClasses = new List<MyRuntimeClass>(); std::vector
<MyNamespace::MyRuntimeClass>
m_myRuntimeClasses;
Costruzione GUID private static readonly Guid myGuid = new Guid("C380465D-2271-428C-9B83-ECEA3B4A85C1"); winrt::guid myGuid{ 0xC380465D, 0x2271, 0x428C, { 0x9B, 0x83, 0xEC, 0xEA, 0x3B, 0x4A, 0x85, 0xC1} };
Separatore dello spazio dei nomi A.B.T A::B::T
Null null nullptr Conversione del metodo UpdateStatus
Ottenere un oggetto di tipo typeof(MyType) winrt::xaml_typename<MyType>() Conversione della proprietà Scenarios
Dichiarazione di parametro per un metodo MyType MyType const& Passaggio di parametri
Dichiarazione di parametro per un metodo asincrono MyType MyType Passaggio di parametri
Chiamare un metodo statico T.Method() T::Method()
Stringhe stringo System.String winrt::hstring Gestione delle stringhe in C++/WinRT
Stringa letterale "a string literal" L"a string literal" Conversione del costruttore, Current e FEATURE_NAME
Tipo inferito (o dedotto) var auto Conversione del metodo BuildClipboardFormatsOutputString
Direttiva Using using A.B.C; using namespace A::B::C; Conversione del costruttore, Current e FEATURE_NAME
Valore letterale stringa verbatim/raw @"verbatim string literal" LR"(raw string literal)" Conversione del metodo DisplayToast

Note

Se un file di intestazione non contiene una using namespace direttiva per uno spazio dei nomi specifico, sarà necessario qualificare completamente tutti i nomi di tipo per tale spazio dei nomi o almeno qualificarli sufficientemente per il compilatore per trovarli. Per un esempio, vedere Conversione del metodo DisplayToast.

Migrazione di classi e membri

Sarà necessario decidere, per ogni tipo C#, se eseguirne il porting a un tipo Windows Runtime oppure a una normale classe/struct/enumerazione C++. Per ulteriori informazioni e per esempi dettagliati che illustrano come prendere tali decisioni, vedere Eseguire il porting dell'esempio Clipboard a C++/WinRT da C#.

Una proprietà C# in genere diventa una funzione di accesso, una funzione di modifica e un campo dati sottostante. Per altre info e un esempio, vedi Conversione della proprietà IsClipboardContentChangedEnabled.

Per i campi non-statici, definiscili come membri dati del tipo di implementazione.

Un campo statico di C# diventa una funzione di accesso statica in C++/WinRT e/o una funzione impostatrice. Per altre info e un esempio, vedi Conversione del costruttore, Current e FEATURE_NAME.

Per le funzioni membro, anche in questo caso, è necessario decidere per ognuna se debba essere inclusa nell'IDL oppure se si tratti di una funzione membro pubblica o privata del tipo di implementazione. Per altre info ed esempi su come decidere, vedi IDL per il tipo MainPage.

Porting del codice XAML e dei file di risorse

Nel caso della conversione dell'esempio degli Appunti in C++/WinRT da C#, è stato possibile usare lo stesso markup XAML (incluse le risorse) e i file di asset nel progetto C# e C++/WinRT. In alcuni casi, le modifiche apportate al markup saranno necessarie per ottenere tale risultato. Vedi Copiare il codice XAML e gli stili necessari per completare la conversione di MainPage.

Modifiche che coinvolgono procedure all'interno del linguaggio

Category C# C++/WinRT Vedere anche
Gestione della durata in un metodo asincrono N/A auto lifetime{ get_strong() }; oppure
auto lifetime = get_strong();
Conversione del metodo CopyButton_Click
Disposizione using (var t = v) auto t{ v };
t.Close(); // or let wrapper destructor do the work
Conversione del metodo CopyImage
Costruisci oggetto new MyType(args) MyType{ args } oppure
MyType(args)
Conversione della proprietà Scenarios
Creare riferimenti non inizializzati MyType myObject; MyType myObject{ nullptr }; oppure
MyType myObject = nullptr;
Conversione del costruttore, Current e FEATURE_NAME
Costruire un oggetto in una variabile con argomenti var myObject = new MyType(args); auto myObject{ MyType{ args } }; o
auto myObject{ MyType(args) }; o
auto myObject = MyType{ args }; o
auto myObject = MyType(args); o
MyType myObject{ args }; o
MyType myObject(args);
Conversione del metodo Footer_Click
Costruire un oggetto in una variabile senza argomenti var myObject = new T(); MyType myObject; Conversione del metodo BuildClipboardFormatsOutputString
Sintassi abbreviata di inizializzazione degli oggetti var p = new FileOpenPicker{
    ViewMode = PickerViewMode.List
};
FileOpenPicker p;
p.ViewMode(PickerViewMode::List);
Operazione vettoriale in blocco var p = new FileOpenPicker{
    FileTypeFilter = { ".png", ".jpg", ".gif" }
};
FileOpenPicker p;
p.FileTypeFilter().ReplaceAll({ L".png", L".jpg", L".gif" });
Conversione del metodo CopyButton_Click
Iterare sulla raccolta foreach (var v in c) for (auto&& v : c) Conversione del metodo BuildClipboardFormatsOutputString
Intercettare un'eccezione catch (Exception ex) catch (winrt::hresult_error const& ex) Conversione del metodo PasteButton_Click
Dettagli dell'eccezione ex.Message ex.message() Conversione del metodo PasteButton_Click
Ottenere il valore di una proprietà myObject.MyProperty myObject.MyProperty() Conversione del metodo NotifyUser
Imposta il valore di una proprietà myObject.MyProperty = value; myObject.MyProperty(value);
Incrementare un valore della proprietà myObject.MyProperty += v; myObject.MyProperty(thing.Property() + v);
Per le stringhe, usa un builder
ToString() myObject.ToString() winrt::to_hstring(myObject) ToString()
Da stringa di lingua a stringa di Windows Runtime N/A winrt::hstring{ s }
Creazione di stringhe StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Costruzione di stringhe
Interpolazione di stringhe $"{i++}) {s.Title}" winrt::to_hstring, e/o winrt::hstring::operator+ Conversione del metodo OnNavigatedTo
Stringa vuota per il confronto System.String.Empty winrt::hstring::empty Conversione del metodo UpdateStatus
Creare una stringa vuota var myEmptyString = String.Empty; winrt::hstring myEmptyString{ L"" };
Operazioni del dizionario 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)
Conversione dei tipi (throw in caso di errore) (MyType)v v.as<MyType>() Conversione del metodo Footer_Click
Conversione di tipo (null in caso di errore) v as MyType v.try_as<MyType>() Adattamento del metodo PasteButton_Click
Gli elementi XAML con x:Name sono proprietà MyNamedElement MyNamedElement() Migrazione del costruttore, Current e FEATURE_NAME
Passare al thread dell'interfaccia utente CoreDispatcher.RunAsync DispatcherQueue.TryEnqueue, oppure winrt::resume_foreground Conversione del metodo NotifyUser e conversione del metodo HistoryAndRoaming
Costruzione di elementi dell'interfaccia utente in codice imperativo in una pagina XAML Vedi Costruzione di elementi dell'interfaccia utente Vedi Costruzione di elementi dell'interfaccia utente

Le sezioni seguenti illustrano in modo più dettagliato alcuni elementi della tabella.

Costruzione di elementi dell'interfaccia utente

Questi esempi di codice mostrano la costruzione di un elemento dell'interfaccia utente nel codice imperativo di una pagina XAML.

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

ToString()

I tipi C# forniscono il metodo Object.ToString .

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

C++/WinRT non fornisce direttamente questa funzionalità, ma puoi passare a alternative.

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

C++/WinRT supporta anche winrt::to_hstring per un numero limitato di tipi. È necessario aggiungere sovraccarichi per tutti gli eventuali tipi aggiuntivi che si desidera convertire in stringa.

Language Converti int in stringa Convertire l'enumerazione in una stringa
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);

Nel caso in cui si voglia convertire un enum in stringa, sarà necessario fornire l'implementazione di 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));
        }
    }
}

Queste conversioni in stringa vengono spesso utilizzate implicitamente dal data binding.

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

Questi binding eseguiranno la conversione winrt::to_hstring della proprietà associata. Nel caso del secondo esempio ( statusEnum), è necessario fornire il proprio overload di winrt::to_hstring, in caso contrario verrà visualizzato un errore del compilatore.

Vedere anche Conversione del metodo Footer_Click.

Creazione di stringhe

Per la compilazione di stringhe, C# ha un tipo StringBuilder predefinito.

Category C# C++/WinRT
Creazione di stringhe StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Aggiungere una stringa Windows Runtime, mantenendo i valori Null builder.Append(s); builder << std::wstring_view{ s };
Aggiungere una nuova riga builder.Append(Environment.NewLine); builder << std::endl;
Accedere al risultato s = builder.ToString(); ws = builder.str();

Vedere anche Conversione del metodo BuildClipboardFormatsOutputString e Conversione del metodo DisplayChangedFormats.

Esecuzione del codice nel thread principale dell'interfaccia utente

Questo esempio è tratto dall'esempio dello scanner di codice a barre.

Quando vuoi lavorare sul thread principale dell'interfaccia utente in un progetto C#, in genere usi il metodo DispatcherQueue.TryEnqueue (o coreDispatcher.RunAsync precedente in UWP). Ecco l'aspetto del modello in C#.

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

È molto più semplice esprimerlo in C++/WinRT. Si noti che accettiamo i parametri per valore, partendo dal presupposto che vorremo accedervi dopo il primo punto di sospensione (in questo caso, co_await). Per altre info, vedi Passaggio di parametri.

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

Se è necessario eseguire il lavoro con una priorità diversa da quella predefinita, consultare la funzione winrt::resume_foreground, che ha un overload che accetta una priorità. Per esempi di codice che mostrano come attendere il completamento di una chiamata a winrt::resume_foreground, vedi Programmazione tenendo conto dell'affinità dei thread.

Definire le classi di runtime in IDL

Vedere IDL per il tipo MainPage e Consolidare i .idl file.

Includere i file di intestazione dello spazio dei nomi C++/WinRT Windows necessari

In C++/WinRT, ogni volta che si desidera usare un tipo di un namespace di Windows, è necessario includere il file header C++/WinRT del corrispondente namespace di Windows. Per un esempio, vedere Conversione del metodo NotifyUser.

Boxing e unboxing

C# converte automaticamente i valori scalari in oggetti. C++/WinRT richiede di chiamare in modo esplicito la funzione winrt::box_value . Entrambe le lingue richiedono che si esegua esplicitamente l'unboxing. Vedi Boxing e unboxing con C++/WinRT.

Nelle tabelle seguenti si useranno queste definizioni.

C# C++/WinRT
int i; int i;
string s; winrt::hstring s;
object o; IInspectable o;
Operation C# C++/WinRT
Pugilato o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
Apertura della confezione i = (int)o;
s = (string)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

C++/CX e C# generano eccezioni se si tenta di eseguire l'unboxing di un puntatore null in un tipo di valore. C++/WinRT considera questo un errore di programmazione e va in crash. In C++/WinRT usa la funzione winrt::unbox_value_or se vuoi gestire il caso in cui l'oggetto non sia del tipo che pensavi fosse.

Scenario C# C++/WinRT
Eseguire l'unboxing di un numero intero noto i = (int)o; i = unbox_value<int>(o);
Se o è null System.NullReferenceException Scontro
Se o non è un boxed int System.InvalidCastException Scontro
Eseguire l'unboxing di int, usare il valore di riserva se è null; generare un arresto anomalo se è qualsiasi altra cosa i = o != null ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Unbox int, se possibile; usare il fallback per qualsiasi altro elemento i = as int? ?? fallback; i = unbox_value_or<int>(o, fallback);

Per un esempio, vedere Conversione del metodo OnNavigatedTo e Conversione del metodo Footer_Click.

Boxing e unboxing di una stringa

Una stringa è in alcuni modi un tipo di valore e in altri modi un tipo riferimento. C# e C++/WinRT trattano le stringhe in modo diverso.

Il tipo ABI HSTRING è un puntatore a una stringa con conteggio dei riferimenti. Ma non deriva da IInspectable, quindi non è tecnicamente un oggetto. Inoltre, una HSTRING null rappresenta la stringa vuota. Il boxing di elementi non derivati da IInspectable viene eseguito eseguendo il wrapping all'interno di un oggetto IReference<T> e il Windows Runtime fornisce un'implementazione standard sotto forma di oggetto PropertyValue (i tipi personalizzati vengono segnalati come PropertyType::OtherType).

C# rappresenta una stringa Windows Runtime come tipo riferimento, mentre C++/WinRT proietta una stringa come tipo valore. Ciò significa che una stringa null boxed può avere rappresentazioni diverse a seconda di come ci si è arrivati.

Behavior C# C++/WinRT
Dichiarazioni object o;
string s;
IInspectable o;
hstring s;
Categoria tipo stringa Tipo di riferimento Tipo di valore
null HSTRING come proietta "" hstring{}
Sono null e "" identici? No Yes
Validità di null s = null;
s.Length genera NullReferenceException
s = hstring{};
s.size() == 0 (valido)
Se si assegna una stringa Null all'oggetto o = (string)null;
o == null
o = box_value(hstring{});
o != nullptr
Se si assegna "" all'oggetto o = "";
o != null
o = box_value(hstring{L""});
o != nullptr

Concetti di base di boxing e unboxing.

Operation C# C++/WinRT
Incapsulare una stringa o = s;
La stringa vuota diventa un oggetto non Null.
o = box_value(s);
La stringa vuota diventa un oggetto non Null.
Eseguire l'unboxing di una stringa nota s = (string)o;
L'oggetto Null diventa una stringa Null.
InvalidCastException se non è una stringa.
s = unbox_value<hstring>(o);
Arresti anomali causati da oggetti nulli.
Arresto anomalo se non è una stringa.
Eseguire l'unboxing di una possibile stringa s = o as string;
Un oggetto null o un valore che non è una stringa diventa una stringa null.

OR

s = o as string ?? fallback;
Un valore nullo o non di tipo stringa diventa un valore di fallback.
Stringa vuota conservata.
s = unbox_value_or<hstring>(o, fallback);
Un valore nullo o non di tipo stringa diventa il valore predefinito.
Stringa vuota mantenuta.

Rendere disponibile una classe per l'estensione di markup {Binding}

Se si intende utilizzare l'estensione di markup {Binding} per associare i dati al proprio tipo di dati, vedere Binding object declared using {Binding}.

Utilizzo di oggetti nel markup XAML

In un progetto C# puoi usare membri privati e elementi denominati dal markup XAML. In C++/WinRT, tuttavia, tutte le entità utilizzate tramite l'estensione di markup XAML {x:Bind} devono essere esposte pubblicamente in IDL.

Inoltre, l'associazione a un valore booleano visualizza true o false in C#, ma mostra Windows. Foundation.IReference'1<Boolean> in C++/WinRT.

Per altre info ed esempi di codice, vedi Utilizzo di oggetti dal markup.

Rendere disponibile un'origine dati per il markup XAML

In C++/WinRT versione 2.0.190530.8 o successiva , winrt::single_threaded_observable_vector crea un vettore osservabile che supporta sia IObservableVector<T> che IObservableVector<IInspectable>. Per un esempio, vedere Conversione della proprietà Scenarios.

È possibile scrivere il file MIDL (.idl) in questo modo (vedere anche Spostare le classi di runtime in file MIDL (.idl)).

namespace Bookstore
{
    runtimeclass BookSku { ... }

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

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

E implementalo così.

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

Per ulteriori informazioni, vedere controlli degli elementi XAML; associazione a una raccolta C++/WinRT, e Raccolte con C++/WinRT.

Rendere disponibile un'origine dati per il markup XAML (prima di C++/WinRT 2.0.190530.8)

Il data binding XAML richiede che l'origine degli elementi implementi IIterable<IInspectable>, nonché una delle combinazioni seguenti di interfacce.

  • IObservableVector<IInspectable>
  • IBindableVector e INotifyCollectionChanged
  • IBindableVector e IBindableObservableVector
  • IBindableVector da solo (non risponderà alle modifiche)
  • IVector<IInspectable>
  • IBindableIterable (eseguirà l'iterazione e salverà gli elementi in una raccolta privata)

Non è possibile rilevare un'interfaccia generica come IVector<T> in fase di esecuzione. Ogni IVector<T> ha un identificatore di interfaccia diverso (IID), che è una funzione di T. Qualsiasi sviluppatore può espandere il set di T in modo arbitrario, quindi chiaramente il codice di associazione XAML non può mai conoscere il set completo per cui eseguire la query. Questa restrizione non è un problema per C# perché ogni oggetto CLR che implementa IEnumerable<T> implementa automaticamente IEnumerable. A livello di ABI, significa che ogni oggetto che implementa IObservableVector<T> implementa automaticamente IObservableVector<IInspectable>.

C++/WinRT non offre tale garanzia. Se una classe di runtime C++/WinRT implementa IObservableVector<T>, non possiamo presupporre che venga fornita anche un'implementazione di IObservableVector<IInspectable> .

Di conseguenza, ecco come verrà visualizzato l'esempio precedente.

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

E l'implementazione.

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

Se è necessario accedere agli oggetti in m_bookSkus, sarà necessario tornare a 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;
}

Classi derivate

Per derivare da una classe di runtime, la classe base deve essere componibile. C# non richiede alcuna procedura speciale per rendere le classi componibili, ma C++/WinRT. Usare la parola chiave unsealed per indicare che si vuole che la classe sia utilizzabile come classe di base.

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

Nel file di intestazione per il tipo di implementazione è necessario includere il file di intestazione della classe di base prima di includere l'intestazione generata automaticamente per la classe derivata. In caso contrario, si otterranno errori come "Uso non valido di questo tipo come espressione".

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

API importanti