Passare a C++/WinRT da C#

Suggerimento

Se si è già letto questo argomento e si torna a consultarlo per eseguire un'attività specifica, è possibile passare direttamente alla sezione Trovare il contenuto in base all'attività da eseguire.

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

Per un case study relativo alla conversione di uno degli esempi di app UWP (Universal Windows Platform), vedi l'argomento complementare Conversione dell'esempio Clipboard da C++/WinRT from C#. Puoi esercitarti e acquisire esperienza seguendo la procedura dettagliata ed eseguendo la conversione dell'esempio mentre procedi.

Preparazione e modifiche attese

Il case study Conversione dell'esempio Clipboard da C++/WinRT from C# illustra alcuni esempi dei tipi di decisioni che dovrai prendere in merito alla progettazione del software durante la conversione di un progetto in C++/WinRT. È quindi consigliabile prepararsi a eseguire la conversione approfondendo le conoscenze sul funzionamento del codice esistente. Otterrai così una panoramica completa delle funzionalità dell'app e della struttura del codice e le decisioni che prenderai ti consentiranno sempre progredire nella giusta direzione.

Per quanto riguarda le modifiche attese dalla conversione, è possibile raggrupparle in quattro categorie.

  • Conversione della proiezione del linguaggio. Windows Runtime (WinRT) viene proiettato in diversi linguaggi di programmazione. Ognuna di queste proiezioni viene progettata in modo da essere coerente con il linguaggio di programmazione in questione. Per C#, alcuni tipi di Windows Runtime vengono proiettati come tipi .NET. Ad esempio, System.Collections.Generic.IReadOnlyList<T> verrà riconvertito in Windows.Foundation.Collections.IVectorView<T>. Inoltre, in C# alcune operazioni di Windows Runtime vengono proiettate come funzionalità utili del linguaggio C#. Ad esempio, in C# si usa la sintassi dell'operatore += per registrare un delegato di gestione degli eventi. Le funzionalità del linguaggio come queste verranno quindi riconvertite nell'operazione fondamentale che viene eseguita (registrazione degli eventi, in questo esempio).
  • Conversione della sintassi del linguaggio. Molte di queste modifiche sono semplici trasformazioni meccaniche che sostituiscono un simbolo con un altro, ad esempio un punto (.) con due doppi punti (::).
  • Conversione della procedura del linguaggio. In alcuni casi può trattarsi di modifiche semplici e ripetitive, ad esempio la modifica di myObject.MyProperty in myObject.MyProperty(). In altri casi sono necessarie modifiche più complesse, ad esempio la conversione di una procedura che prevede l'uso di System.Text.StringBuilder in una che prevede l'uso di std::wostringstream.
  • Attività collegate alla conversione specifiche di C++/WinRT. C# si prende cura di determinati dettagli di Windows Runtime in modo implicito, dietro le quinte. Questi dettagli vengono eseguiti in C++/WinRT in modo esplicito. Un esempio è l'uso di un file di .idl per definire le classi di runtime.

Dopo l'indice seguente basato sulle attività, le restanti sezioni di questo argomento sono strutturate in base alla tassonomia precedente.

Trovare il contenuto in base all'attività da eseguire

Attività Contenuto
Creare un componente Windows Runtime È possibile ottenere determinate funzionalità (o determinate API chiamate) solo con C++. È possibile scomporre tali funzionalità in un componente Windows Runtime C++/WinRT e quindi utilizzare il componente, ad esempio, in un'app C#. Vedere Componenti Windows Runtime con C++/WinRT e Se si sta creando una classe di runtime in un componente Windows Runtime.
Convertire un metodo asincrono È consigliabile che la prima riga di un metodo asincrono in una classe di runtime C++/WinRT sia auto lifetime = get_strong(); (vedere Accesso sicuro al puntatore this in una coroutine membro di classe).

Per la conversione da Task, vedere Azione asincrona.
Per la conversione da Task<T>, vedere Operazione asincrona.
Per la conversione da async void, vedere Metodo Fire-and-forget.
Convertire una classe Stabilire prima se la classe deve essere una classe di runtime o se può essere una classe ordinaria. Per facilitare la scelta, vedere la parte iniziale dell'argomento Creare API con C++/WinRT. Vedere quindi le tre righe seguenti.
Convertire una classe di runtime Si tratta di una classe che condivide le funzionalità all'esterno dell'app C++ o di una classe usata nel data binding XAML. Vedere Se si sta creando una classe di runtime in un componente Windows Runtime o Se si sta creando una classe di runtime a cui fare riferimento nell'interfaccia utente XAML.

Questi collegamenti contengono descrizioni più dettagliate, ma una classe di runtime deve essere dichiarata in IDL. Se il progetto contiene già un file IDL (ad esempio, Project.idl), è consigliabile dichiarare in questo file ogni nuova classe di runtime. In IDL dichiarare 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, ricompilare ed esaminare i file stub generati (.h e .cpp) nella cartella Generated Files del progetto (in Esplora soluzioni, con il nodo di progetto selezionato, verificare che l'opzione Mostra tutti i file sia stata attivata). Confrontare i file stub con i file già presenti nel progetto, aggiungendo file o aggiungendo/aggiornando le firme di funzione eventualmente necessarie. La sintassi dei file stub è sempre corretta ed è quindi consigliabile usarla per ridurre il numero di errori di compilazione. Quando gli stub presenti nel progetto corrispondono a quelli presenti nei file stub, è possibile procedere e implementarli convertendo il codice C#.
Convertire una classe ordinaria Vedere Se non si sta creando una classe di runtime.
Creare IDL 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
Convertire una raccolta Gruppi di prodotti con C++/WinRT
Rendere disponibile un'origine dati per il markup XAML
Contenitore associativo
Accesso ai membri del vettore
Convertire un evento Delegato del gestore eventi come membro di classe
Revocare il delegato del gestore eventi
Convertire un metodo Da C#: private async void SampleButton_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) { ... }
Nel file .h di C++/WinRT: fire_and_forget SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&);
Nel file .cpp di C++/WinRT: fire_and_forget OcrFileImage::SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&) {...}
Convertire stringhe Gestione delle stringhe in C++/WinRT
ToString
Compilazione 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 la conversione unboxing non riesce. Vedere anche Boxing e unboxing.

C#: o as Value? ?? fallback
C++/WinRT: unbox_value_or<Value>(o, fallback)
Restituisce il fallback se la conversione unboxing non riesce. Vedere 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 un valore null se la conversione non riesce.

Modifiche che coinvolgono la proiezione del linguaggio

Categoria C# C++/WinRT Vedi anche
Oggetto non tipizzato object o 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 std::vector in qualsiasi punto, usa allora push_back per aggiungere un elemento.
Tipi di raccolta di sola lettura IReadOnlyList<T> IVectorView<T> Conversione del metodo BuildClipboardFormatsOutputString
Delegato del gestore eventi come membro di 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 puoi scegliere tra più opzioni sintattiche per registrare/revocare un delegato del gestore eventi, come illustrato in Gestire eventi mediante i delegati in C++/WinRT. Vedi anche Conversione del metodo EnableClipboardContentChangedNotifications.

In alcuni casi, ad esempio quando il destinatario di un evento (un oggetto che gestisce un evento) sta per essere eliminato definitivamente, è opportuno revocare un gestore dell'evento in modo che l'origine dell'evento (l'oggetto che genera l'evento) non chiami un oggetto eliminato definitivamente. Vedi Revocare un delegato registrato. In casi simili, crea una variabile del membro event_token per i gestori eventi. Per un esempio, vedi 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 tuo tipo di implementazionese vuoi registrarlo nel markup XAML. Se registri un gestore eventi solo in codice imperativo, il gestore eventi non deve necessariamente essere pubblico.

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

In alternativa, puoi associare la pagina XAML di registrazione al tuo tipo di implementazione e rendere privato OpenButton_Click.

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

Uno scenario finale è quello in cui il progetto C# da trasferire si associa al gestore eventi dal markup (per informazioni più approfondite su tale scenario, vedere Funzioni in x:Bind).

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

È sufficiente modificare il markup al più semplice Click="OpenButton_Click". O, se si preferisce, è possibile mantenerlo così com'è. Per supportare questa operazione è sufficiente dichiarare il gestore eventi in IDL.

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

Nota

Dichiarare la funzione come voidse la si stesse implementando in modalità Attivare e poi dimenticare.

Modifiche che coinvolgono la sintassi del linguaggio

Categoria C# C++/WinRT Vedi 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 asincronia) async void ... winrt::fire_and_forget ... Conversione del metodo CopyButton_Click, Fire and forget
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 di 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 type 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 string o System.String winrt::hstring Gestione delle stringhe in C++/WinRT
Valore letterale stringa "a string literal" L"a string literal" Conversione del costruttore Current e di FEATURE_NAME
Tipo dedotto var auto Conversione del metodo BuildClipboardFormatsOutputString
Direttiva using using A.B.C; using namespace A::B::C; Conversione del costruttore Current e di FEATURE_NAME
Valori letterali stringa verbatim/non elaborati @"verbatim string literal" LR"(raw string literal)" Conversione del metodo DisplayToast

Nota

Se un file di intestazione non contiene una direttiva using namespace per un determinato spazio dei nomi, dovrai qualificare completamente tutti i nomi di tipo per tale spazio dei nomi o perlomeno qualificarli in misura sufficiente per consentire al compilatore di trovarli. Per un esempio, vedi Conversione del metodo DisplayToast.

Conversione di classi e membri

Dovrai decidere se convertire ogni tipo C# in un tipo di Windows Runtime o in una normale classe/struct/enumerazione C++. Per altre informazioni ed esempi dettagliati che illustrano come prendere tali decisioni, vedi Conversione dell'esempio Clipboard da C++/WinRT from C#.

Una proprietà C# diventa in genere una funzione di accesso, una funzione mutatore e un membro dati di supporto. Per altre informazioni e un esempio, vedi Conversione della proprietà IsClipboardContentChangedEnabled.

Per i campi non statici, impostali come membri dati del tuo tipo di implementazione.

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

Anche per le funzioni membro dovrai decidere se ognuna appartiene o meno a IDL oppure se si tratta di una funzione membro pubblica o privata del tuo tipo di implementazione. Per altre informazioni ed esempi su come prendere una decisione, vedi IDL per il tipo MainPage.

Conversione del markup XAML e dei file di asset

Nel caso illustrato in Conversione dell'esempio Clipboard da C++/WinRT from C# abbiamo potuto usare lo stesso markup XAML (incluse risorse) e gli stessi file di asset in tutto il progetto C# e C++/WinRT. In alcuni casi, le modifiche apportate al markup saranno necessarie a raggiungere lo scopo. Vedi Copiare il codice XAML e gli stili necessari per completare la conversione di MainPage.

Modifiche che coinvolgono le procedure all'interno del linguaggio

Categoria C# C++/WinRT Vedi anche
Gestione del ciclo di vita in un metodo asincrono N/D auto lifetime{ get_strong() }; oppure
auto lifetime = get_strong();
Conversione del metodo CopyButton_Click
Dismissione using (var t = v) auto t{ v };
t.Close(); // or let wrapper destructor do the work
Conversione del metodo CopyImage
Costruire un oggetto new MyType(args) MyType{ args } oppure
MyType(args)
Conversione della proprietà Scenarios
Creare un riferimento non inizializzato MyType myObject; MyType myObject{ nullptr }; oppure
MyType myObject = nullptr;
Conversione del costruttore Current e di FEATURE_NAME
Costruire un oggetto in una variabile con args var myObject = new MyType(args); auto myObject{ MyType{ args } }; oppure
auto myObject{ MyType(args) }; oppure
auto myObject = MyType{ args }; oppure
auto myObject = MyType(args); oppure
MyType myObject{ args }; oppure
MyType myObject(args);
Conversione del metodo Footer_Click
Costruire un oggetto in una variabile senza args var myObject = new T(); MyType myObject; Conversione del metodo BuildClipboardFormatsOutputString
Abbreviazione dell'inizializzazione dell'oggetto var p = new FileOpenPicker{
    ViewMode = PickerViewMode.List
};
FileOpenPicker p;
p.ViewMode(PickerViewMode::List);
Operazione vettore 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
Eseguire l'iterazione nella raccolta foreach (var v in c) for (auto&& v : c) Conversione del metodo BuildClipboardFormatsOutputString
Recuperare 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
Impostare il valore di una proprietà myObject.MyProperty = value; myObject.MyProperty(value);
Incrementare il valore di una proprietà myObject.MyProperty += v; myObject.MyProperty(thing.Property() + v);
Per le stringhe, passare a un generatore
ToString() myObject.ToString() winrt::to_hstring(myObject) ToString()
Da stringa di linguaggio a stringa di Windows Runtime N/D winrt::hstring{ s }
Compilazione di stringhe StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Compilazione di stringhe
Interpolazione di stringa $"{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 dei tipi (null in caso di errore) v as MyType v.try_as<MyType>() Conversione del metodo PasteButton_Click
Gli elementi XAML con x:Name sono proprietà MyNamedElement MyNamedElement() Conversione del costruttore Current e di FEATURE_NAME
Passare a thread di UI CoreDispatcher.RunAsync CoreDispatcher.RunAsync o 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 illustrano la costruzione di un elemento dell'interfaccia utente nel codice imperativo di una pagina XAML.

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

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 ricorrere ad alternative.

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

C++/WinRT supporta inoltre winrt::to_hstring per un numero limitato di tipi. Dovrai aggiungere gli overload per tutti i tipi aggiuntivi che vuoi convertire in stringhe.

Lingua Conversione in stringa di int Conversione in stringa di enum
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);

In caso di conversione in stringa di un enum, dovrai 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 in modo implicito dal data binding.

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

I binding eseguiranno la funzione winrt::to_hstring sulla proprietà associata. Nel caso del secondo esempio (StatusEnum), devi fornire un overload personalizzato di winrt::to_hstring, altrimenti verrà restituito un errore del compilatore.

Vedi anche Conversione del metodo Footer_Click.

Compilazione di stringhe

Per la compilazione di stringhe, C# dispone di un tipo StringBuilder incorporato.

Categoria C# C++/WinRT
Compilazione di stringhe StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Aggiungere una stringa di 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();

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

Esecuzione del codice nel thread principale dell'interfaccia utente

Questo esempio è basato sul contenuto di Esempio di scanner di codice a barre.

Quando si vuole intervenire sul thread principale dell'interfaccia utente in un progetto C#, si usa in genere il metodo CoreDispatcher.RunAsync, come illustrato di seguito.

private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        // Do work on the main UI thread here.
    });
}

È molto più semplice esprimere questo scenario in C++/WinRT. Si noti che i parametri vengono accettati in base al valore presupponendo che si voglia accedere a tali parametri dopo il primo punto di sospensione (co_await in questo caso). Per altre informazioni, vedere Passaggio dei parametri.

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

Se è necessario intervenire con una priorità diversa da quella predefinita, vedere la funzione winrt::resume_foreground, che ha un overload che accetta una priorità. Per alcuni esempi di codice che illustrano come attendere una chiamata a winrt::resume_foreground, vedere Programmazione basata sull'affinità del thread.

Definire le classi di runtime in IDL

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

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

In C++/WinRT, ogni volta che vuoi usare un tipo di uno spazio dei nomi di Windows, devi includere il file di intestazione dello spazio dei nomi di Windows C++/WinRT corrispondente. Per un esempio, vedi Conversione del metodo NotifyUser.

Boxing e unboxing

C# esegue automaticamente la conversione boxing di valori scalari in oggetti. C++/WinRT richiede che la funzione winrt::box_value venga chiamata esplicitamente. Per entrambi i linguaggi è necessario eseguire l'unboxing esplicito. Vedi Boxing e unboxing con C++/WinRT.

Nelle tabelle seguenti useremo queste definizioni.

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

C++/CX e C# generano eccezioni se provi a eseguire l'unboxing di un puntatore Null in un tipo di valore. C++/WinRT considera questo caso come un errore di programmazione e si arresta in modo anomalo. In C++/WinRT usa la funzione winrt::unbox_value_or se vuoi gestire il caso in cui l'oggetto non è del tipo previsto.

Scenario C# C++/WinRT
Unboxing di un numero intero noto i = (int)o; i = unbox_value<int>(o);
Se o è Null System.NullReferenceException Arresto anomalo
Se o non è un valore int sottoposto a boxing System.InvalidCastException Arresto anomalo
Unboxing di int, fallback se Null e arresto anomalo negli altri casi i = o != null ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Unboxing di int, se possibile, e fallback per gli altri casi i = as int? ?? fallback; i = unbox_value_or<int>(o, fallback);

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

Boxing e unboxing di una stringa

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

Il tipo ABI HSTRING è un puntatore a una stringa con conteggio dei riferimenti. Non deriva tuttavia da IInspectable e pertanto non è tecnicamente un oggetto. Inoltre, un'istanza Null di HSTRING rappresenta la stringa vuota. La conversione boxing di elementi non derivati da IInspectable viene eseguita tramite il wrapping all'interno di un'interfaccia IReference<T> e Windows Runtime offre un'implementazione standard sotto forma di oggetto PropertyValue. I tipi personalizzati vengono segnalati come PropertyType::OtherType.

C# rappresenta una stringa di Windows Runtime come tipo di riferimento, mentre C++/WinRT proietta una stringa come tipo di valore. Ciò significa che una stringa Null sottoposta a boxing può avere rappresentazioni diverse a seconda del modo in cui viene raggiunta.

Comportamento C# C++/WinRT
Dichiarazioni object o;
string s;
IInspectable o;
hstring s;
Categoria del tipo di stringa Tipo di riferimento Tipo di valore
HSTRING Null proiettata come "" hstring{}
Null e "" sono identici? No
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

Boxing e unboxing di base.

Operation C# C++/WinRT
Boxing di una stringa o = s;
Una stringa vuota diventa un oggetto non null.
o = box_value(s);
Una stringa vuota diventa un oggetto non null.
Unboxing di una stringa nota s = (string)o;
Un oggetto null diventa una stringa null.
InvalidCastException se non è una stringa.
s = unbox_value<hstring>(o);
Arresto anomalo dell'oggetto null.
Arresto anomalo se non è una stringa.
Unboxing di una possibile stringa s = o as string;
L'oggetto null o non stringa diventa una stringa null.

OPPURE

s = o as string ?? fallback;
Il valore null o non stringa diventa fallback.
La stringa vuota viene mantenuta.
s = unbox_value_or<hstring>(o, fallback);
Il valore null o non stringa diventa fallback.
La stringa vuota viene mantenuta.

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

Se intendi usare l'estensione di markup {Binding} per il data binding al tipo di dati, vedi Binding di oggetti dichiarati usando {Binding}.

Utilizzo di oggetti dal markup XAML

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

Inoltre, il binding a un valore booleano viene visualizzato come true o false in C#, ma appare come Windows.Foundation.IReference`1<Boolean> in C++/WinRT.

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

Rendere disponibile un'origine dati per il markup XAML

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

Puoi creare il file Midl (.idl) nel modo seguente. Vedi anche Factoring delle classi di runtime nei file Midl (.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; };
    }
}

Esegui quindi l'implementazione nel modo seguente.

// 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 altre informazioni, vedi Controlli di elementi XAML, binding 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)

Per il data binding XAML è necessario che un'origine degli elementi implementi IIterable<IInspectable> e una delle combinazioni di interfacce seguenti.

  • IObservableVector<IInspectable>
  • IBindableVector e INotifyCollectionChanged
  • IBindableVector e IBindableObservableVector
  • IBindableVector da sola (non risponde alle modifiche)
  • IVector<IInspectable>
  • IBindableIterable (esegue l'iterazione e salva gli elementi in una raccolta privata)

Un'interfaccia generica come IVector<T> non può essere rilevata in fase di runtime. Ogni IVector<T> ha un identificatore di interfaccia (IID) diverso, che è una funzione di T. Gli sviluppatori possono espandere il set di T in modo arbitrario e quindi il codice di binding XAML non può mai conoscere il set completo per l'esecuzione di query. Questa restrizione non è un problema per C# perché ogni oggetto CLR che implementa IEnumerable<T> implementa automaticamente anche IEnumerable. A livello di ABI, ciò significa che ogni oggetto che implementa IObservableVector<T> implementa automaticamente anche IObservableVector<IInspectable>.

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

Ecco quindi come dovrebbe essere l'esempio precedente.

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

Ed ecco 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 hai la necessità di accedere ad oggetti in m_bookSkus, devi eseguire una chiamata a QI su tali oggetti in 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 essere derivata da una classe di runtime, la classe di base deve essere componibile. In C# non è necessario eseguire operazioni particolari per rendere componibili le classi, diversamente da C++/WinRT. Per indicare che una classe può essere usata come classe di base devi usare la parola chiave unsealed.

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

Nel file di intestazione del tuo tipo di implementazione devi includere il file di intestazione della classe di base prima dell'intestazione generata automaticamente per la classe derivata. In caso contrario, verranno restituiti errori come "Tipo non valido 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