Migrar a C++/WinRT desde C#Move to C++/WinRT from C#

En este tema se catalogan de forma exhaustiva los detalles técnicos implicados en la migración del código fuente de un proyecto de C# a su equivalente de C++/WinRT.This topic comprehensively catalogs the technical details involved in porting the source code in a C# project to its equivalent in C++/WinRT.

Para ver un caso práctico de migración de uno de los ejemplos de aplicaciones de la Plataforma universal de Windows (UWP), consulta el tema complementario Migración del ejemplo de Clipboard a C++/WinRT desde C#.For a case study of porting one of the Universal Windows Platform (UWP) app samples, see the companion topic Porting the Clipboard sample to C++/WinRT from C#. Para ganar práctica y experiencia en la migración, puedes seguir ese tutorial y portar el ejemplo por tu cuenta a medida que avanzas.You can gain porting practice and experience by following along with that walkthrough, and porting the sample for yourself as you go.

Cómo realizar la preparación y qué esperarHow to prepare, and what to expect

En el caso práctico del tema Migración del ejemplo de Clipboard a C++/WinRT desde C#, se muestran ejemplos de los tipos de decisiones de diseño de software que deberás tomar al portar un proyecto a C++/WinRT.The case study Porting the Clipboard sample to C++/WinRT from C# illustrates examples of the kinds of software design decisions that you'll make while porting a project to C++/WinRT. Por lo tanto, te recomendamos que, para prepararte para la migración, obtengas conocimientos detallados sobre cómo funciona el código existente.So, it's a good idea to prepare for porting by gaining a solid understanding of how the existing code works. De este modo, contarás con información general adecuada sobre la funcionalidad de la aplicación y la estructura del código y, luego, las decisiones que tomes siempre te ayudarán a avanzar en la dirección adecuada.That way, you'll get a good overview of the app's functionality, and the code's structure, and then the decisions that you make will always take you forward, and in the right direction.

En lo que se refiere a los tipos de cambio en la migración que debes esperar, puedes agruparlos en cuatro categorías.In terms of what kinds of porting changes to expect, you could group them into four categories.

  • Migración de la proyección del lenguaje.Port the language projection. Windows Runtime (WinRT) se proyecta en varios lenguajes de programación.The Windows Runtime (WinRT) is projected into various programming languages. Cada una de esas proyecciones de lenguaje está diseñada para que suene idiomática en el lenguaje de programación en cuestión.Each of those language projections is designed to feel idiomatic to the programming language in question. En C#, algunos tipos de Windows Runtime se proyectan como tipos .NET.For C#, some Windows Runtime types are projected as .NET types. Por ejemplo, traducirás System.Collections.Generic.IReadOnlyList<T> de nuevo a Windows.Foundation.Collections.IVectorView<T> .So for example you'll be translating System.Collections.Generic.IReadOnlyList<T> back to Windows.Foundation.Collections.IVectorView<T>. También en C#, algunas operaciones de Windows Runtime se proyectan como características de lenguaje adecuadas de C#.Also in C#, some Windows Runtime operations are projected as convenient C# language features. Un ejemplo sería el uso de la sintaxis de operador += en C# para registrar un delegado de control de eventos.An example is that in C# you use the += operator syntax to register an event-handling delegate. Por lo tanto, traducirás características del lenguaje; por ejemplo, para volver a la operación fundamental que se está realizando (el registro de eventos, en este caso).So you'll be translating language features such as that back to the fundamental operation that's being performed (event registration, in this example).
  • Migración de la sintaxis del lenguaje.Port language syntax. Muchos de estos cambios son transformaciones mecánicas simples, en las que se reemplaza un símbolo por otro.Many of these changes are simple mechanical transforms, replacing one symbol for another. Por ejemplo, se cambia un punto (.) por dos signos de dos puntos (::).For example, changing dot (.) to double-colon (::).
  • Procedimiento de migración del lenguaje.Port language procedure. Algunos de estos pueden ser cambios sencillos y repetitivos (como de myObject.MyProperty a myObject.MyProperty()).Some of these can be simple, repetitive changes (such as myObject.MyProperty to myObject.MyProperty()). Otros requieren cambios más complejos (por ejemplo, portar un procedimiento que implique el uso de System.Text.StringBuilder a uno que implique el uso de std::wostringstream).Others need deeper changes (for example, porting a procedure that involves the use of System.Text.StringBuilder to one that involves the use of std::wostringstream).
  • Tareas relacionadas con la portación específicas de C++/WinRT.Porting-related tasks that are specific to C++/WinRT. C# se encarga de completar determinados detalles de Windows Runtime de manera implícita y en segundo plano.Certain details of the Windows Runtime are taken care of impliclicly by C#, behind the scenes. Estos detalles se realizan explícitamente en C++/WinRT.Those details are done explicitly in C++/WinRT. Un ejemplo sería el uso de un archivo .idl para definir las clases en tiempo de ejecución.An example is that you use an .idl file to define your runtime classes.

El resto de este tema está estructurado según esa taxonomía.The rest of this topic is structured according to that taxonomy.

Cambios relacionados con la proyección de lenguajeChanges that involve the language projection

CategoryCategory C#C# C++/WinRTC++/WinRT Consulta tambiénSee also
Objeto sin tipoUntyped object object o System.Objectobject, or System.Object Windows::Foundation::IInspectableWindows::Foundation::IInspectable Migración del método EnableClipboardContentChangedNotificationsPorting the EnableClipboardContentChangedNotifications method
Espacios de nombres de proyecciónProjection namespaces using System; using namespace Windows::Foundation;
using System.Collections.Generic; using namespace Windows::Foundation::Collections;
Tamaño de una colecciónSize of a collection collection.Count collection.Size() Migración del método BuildClipboardFormatsOutputStringPorting the BuildClipboardFormatsOutputString method
Tipo de colección típicoTypical collection type IList<T> y Agregar para agregar un elemento.IList<T>, and Add to add an element. IVector<T> y Anexar para agregar un elemento.IVector<T>, and Append to add an element. Si usa un elemento std::vector en alguna parte, emplee push_back para agregar un elemento.If you use a std::vector anywhere, then push_back to add an element.
Tipo de colección de solo lecturaRead-only collection type IReadOnlyList<T> IReadOnlyList<T> IVectorView<T> IVectorView<T> Migración del método BuildClipboardFormatsOutputStringPorting the BuildClipboardFormatsOutputString method
Delegado del controlador de eventos como miembro de claseEvent handler delegate as class member myObject.EventName += Handler; token = myObject.EventName({ get_weak(), &Class::Handler }); Migración del método EnableClipboardContentChangedNotificationsPorting the EnableClipboardContentChangedNotifications method
Delegado del controlador de eventos de revocaciónRevoke event handler delegate myObject.EventName -= Handler; myObject.EventName(token); Migración del método EnableClipboardContentChangedNotificationsPorting the EnableClipboardContentChangedNotifications method
Contenedor asociativoAssociative container IDictionary<K, V> IDictionary<K, V> IMap<K, V> IMap<K, V>
Acceso de miembro vectorialVector member access x = v[i];
v[i] = x;
x = v.GetAt(i);
v.SetAt(i, x);

Registro/revocación de un controlador de eventosRegister/revoke an event handler

En C++/WinRT, tienes varias opciones sintácticas para registrar o revocar un delegado del controlador de eventos, tal como se describe en Control de eventos mediante delegados en C++/WinRT.In C++/WinRT, you have several syntactic options to register/revoke an event handler delegate, as described in Handle events by using delegates in C++/WinRT. Consulta también el tema Migración del método EnableClipboardContentChangedNotifications.Also see Porting the EnableClipboardContentChangedNotifications method.

A veces, por ejemplo, cuando el destinatario de un evento (un objeto que controla un evento) esté a punto de destruirse, te recomendamos que revoques un controlador de eventos para que el origen del evento (el objeto que provoca el evento) no llame a un objeto destruido.Sometimes, for example when an event recipient (an object handling an event) is about to be destroyed, you'll want to revoke an event handler so that the event source (the object raising the event) doesn't call into a destroyed object. Consulta Revocación de un delegado registrado.See Revoke a registered delegate. En estos casos, crea una variable de miembro event_token para los controladores de eventos.In cases like that, create an event_token member variable for your event handlers. Para ver un ejemplo, consulta el tema Migración del método EnableClipboardContentChangedNotifications.For an example, see Porting the EnableClipboardContentChangedNotifications method.

También puedes registrar un controlador de eventos en el marcado XAML.You can also register an event handler in XAML markup.

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

En C#, el método OpenButton_Click puede ser privado, y XAML aún podrá conectarlo al evento ButtonBase.Click generado por OpenButton.In C#, your OpenButton_Click method can be private, and XAML will still be able to connect it to the ButtonBase.Click event raised by OpenButton.

En C++/WinRT, el método OpenButton_Click debe ser público en el tipo de implementación si quieres registrarlo en el marcado XAML.In C++/WinRT, your OpenButton_Click method must be public in your implementation type if you want to register it in XAML markup. Si solo registras un controlador de eventos en código imperativo, no es necesario que el controlador de eventos sea público.If you register an event handler only in imperative code, then the event handler doesn't need to be public.

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

Como alternativa, puedes hacer que la página XAML de registro sea compatible con el tipo de implementación, y convertir el método OpenButton_Click en privado.Alternatively, you can make the registering XAML page a friend of your implementation type, and OpenButton_Click private.

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

Un escenario final es donde el proyecto de C# que estás portando se enlaza al controlador de eventos desde el marcado (para más información sobre este escenario, consulta Funciones de x:Bind).One final scenario is where the C# project that you're porting binds to the event handler from markup (for more background on that scenario, see Functions in x:Bind).

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

Puedes cambiarlo al marcado Click="OpenButton_Click" más sencillo.You could just change that markup to the more simple Click="OpenButton_Click". O bien, si lo prefieres, puedes mantener el marcado tal como está.Or, if you prefer, you can keep that markup as it is. Todo lo que tienes que hacer para admitirlo es declarar el controlador de eventos en IDL.All you have to do to support it is to declare the event handler in IDL.

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

Nota

Declara la función como void incluso si la implementas como Desencadenamiento y olvido.Declare the function as void even if you implement it as Fire and forget.

Cambios relacionados con la sintaxis del lenguajeChanges that involve the language syntax

CategoryCategory C#C# C++/WinRTC++/WinRT Consulta tambiénSee also
Modificadores de accesoAccess modifiers public \<member\> public:
    \<member\>
Migración del método Button_ClickPorting the Button_Click method
Acceso a un miembro de datosAccess a data member this.variable this->variable
Acción asincrónicaAsync action async Task ... IAsyncAction ...
Operación asincrónicaAsync operation async Task<T> ... IAsyncOperation<T> ...
Método Fire-and-forget (implica una asincronía)Fire-and-forget method (implies async) async void ... winrt::fire_and_forget ... Migración del método CopyButton_ClickPorting the CopyButton_Click method
Acceso a una constante enumeradaAccess an enumerated constant E.Value E::Value Migración del método DisplayChangedFormatsPorting the DisplayChangedFormats method
Espera cooperativaCooperatively wait await ... co_await ... Migración del método CopyButton_ClickPorting the CopyButton_Click method
Colección de tipos proyectados como campo privadoCollection of projected types as a private field private List<MyRuntimeClass> myRuntimeClasses = new List<MyRuntimeClass>(); std::vector
<MyNamespace::MyRuntimeClass>
m_myRuntimeClasses;
Construcción de GUIDGUID construction 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} };
Separador de espacios de nombresNamespace separator A.B.T A::B::T
NullNull null nullptr Migración del método UpdateStatusPorting the UpdateStatus method
Obtención de un objeto typeObtain a type object typeof(MyType) winrt::xaml_typename<MyType>() Migración de la propiedad ScenariosPorting the Scenarios property
Declaración de parámetros para un métodoParameter declaration for a method MyType MyType const& Parameter-passingParameter-passing
Declaración de parámetros para un método asincrónicoParameter declaration for an async method MyType MyType Parameter-passingParameter-passing
Llamada a un método estáticoCall a static method T.Method() T::Method()
CadenasStrings string o System.Stringstring, or System.String winrt::hstringwinrt::hstring Control de cadenas en C++/WinRTString handling in C++/WinRT
Literal de cadenaString literal "a string literal" L"a string literal" Migración del constructor, Current y FEATURE_NAMEPorting the constructor, Current, and FEATURE_NAME
Tipo inferido (o deducido)Inferred (or deduced) type var auto Migración del método BuildClipboardFormatsOutputStringPorting the BuildClipboardFormatsOutputString method
Using-directiveUsing-directive using A.B.C; using namespace A::B::C; Migración del constructor, Current y FEATURE_NAMEPorting the constructor, Current, and FEATURE_NAME
Literal de cadena sin formato/textualVerbatim/raw string literal @"verbatim string literal" LR"(raw string literal)" Migración del método DisplayToastPorting the DisplayToast method

Nota

Si un archivo de encabezado no contiene una directiva de using namespace para un espacio de nombres determinado, deberás calificar completamente todos los nombres de tipo de ese espacio de nombre, o por lo menos lo suficiente como para que el compilador los pueda encontrar.If a header file doesn't contain a using namespace directive for a given namespace, then you'll have to fully-qualify all type names for that namespace; or at least qualify them sufficiently for the compiler to find them. Para obtener un ejemplo, consulta Migración del método DisplayToast.For an example, see Porting the DisplayToast method.

Migración de clases y miembrosPorting classes and members

Para cada tipo de C#, deberás decidir si quieres portarlo a un tipo de Windows Runtime o a una clase, estructura o enumeración de C++ normal.You'll need to decide, for each C# type, whether to port it to a Windows Runtime type, or to a regular C++ class/struct/enumeration. Para obtener más información y ejemplos detallados en los que se muestra cómo tomar esas decisiones, consulta el tema Migración del ejemplo de Clipboard a C++/WinRT desde C#.For more info, and detailed examples illustrating how to make those decisions, see Porting the Clipboard sample to C++/WinRT from C#.

Una propiedad de C#, normalmente, se convierte en una función de descriptor de acceso, una función mutadora y un miembro de datos de respaldo.A C# property typically becomes an accessor function, a mutator function, and a backing data member. Para obtener más información y un ejemplo, consulta el tema Migración de la propiedad IsClipboardContentChangedEnabled.For more info, and an example, see Porting the IsClipboardContentChangedEnabled property.

En el caso de los campos no estáticos, haz que sean miembros de datos del tipo de implementación.For non-static fields, make them data members of your implementation type.

Un campo estático de C# se convierte en una función mutadora o un descriptor de acceso estático de C++/WinRT.A C# static field becomes a C++/WinRT static accessor and/or mutator function. Para obtener más información y un ejemplo, consulta el tema Migración del constructor, Current y FEATURE_NAME.For more info, and an example, see Porting the constructor, Current, and FEATURE_NAME.

En el caso de las funciones de miembro, de nuevo deberás decidir si cada una de ellas forma parte o no de IDL, o si se trata de una función de miembro pública o privada del tipo de implementación.For member functions, again, you'll need to decide for each one whether or not it belongs in the IDL, or whether it's a public or private member function of your implementation type. Para obtener más información y ejemplos sobre cómo decidirlo, consulta el tema IDL para el tipo MainPage.For more info, and examples of how to decide, see IDL for the MainPage type.

Migración del marcado XAML y los archivos de recursosPorting XAML markup, and asset files

En el tema Migración del ejemplo de Clipboard a C++/WinRT desde C#, pudimos usar el mismo marcado XAML (incluidos los recursos) y los archivos de recursos en el proyecto de C# y de C++/WinRT.In the case of Porting the Clipboard sample to C++/WinRT from C#, we were able to use the same XAML markup (including resources) and asset files across the C# and the C++/WinRT project. En algunos casos, se requerirán ediciones en el marcado para lograrlo.In some cases, edits to markup will be necessary to achieve that. Consulta el tema Copia de XAML y estilos necesarios para terminar de portar MainPage.See Copy the XAML and styles necessary to finish up porting MainPage.

Cambios que implican procedimientos del lenguajeChanges that involve procedures within the language

CategoryCategory C#C# C++/WinRTC++/WinRT Consulta tambiénSee also
Administración de la duración en un método asincrónicoLifetime management in an async method N/AN/A auto lifetime{ get_strong() }; oauto lifetime{ get_strong() }; or
auto lifetime = get_strong();
Migración del método CopyButton_ClickPorting the CopyButton_Click method
EliminaciónDisposal using (var t = v) auto t{ v };
t.Close(); // or let wrapper destructor do the work
Migración del método CopyImagePorting the CopyImage method
Construcción del objetoConstruct object new MyType(args) MyType{ args } oMyType{ args } or
MyType(args)
Migración de la propiedad ScenariosPorting the Scenarios property
Creación de una referencia no inicializadaCreate uninitialized reference MyType myObject; MyType myObject{ nullptr }; oMyType myObject{ nullptr }; or
MyType myObject = nullptr;
Migración del constructor, Current y FEATURE_NAMEPorting the constructor, Current, and FEATURE_NAME
Construcción de un objeto en una variable con argumentosConstruct object into variable with args var myObject = new MyType(args); auto myObject{ MyType{ args } }; oauto myObject{ MyType{ args } }; or
auto myObject{ MyType(args) }; oauto myObject{ MyType(args) }; or
auto myObject = MyType{ args }; oauto myObject = MyType{ args }; or
auto myObject = MyType(args); oauto myObject = MyType(args); or
MyType myObject{ args }; oMyType myObject{ args }; or
MyType myObject(args);
Migración del método Footer_ClickPorting the Footer_Click method
Construcción de un objeto en una variable sin argumentosConstruct object into variable without args var myObject = new T(); MyType myObject; Migración del método BuildClipboardFormatsOutputStringPorting the BuildClipboardFormatsOutputString method
Abreviatura de la inicialización del objetoObject initialization shorthand var p = new FileOpenPicker{
    ViewMode = PickerViewMode.List
};
FileOpenPicker p;
p.ViewMode(PickerViewMode::List);
Operación del vector masivaBulk vector operation var p = new FileOpenPicker{
    FileTypeFilter = { ".png", ".jpg", ".gif" }
};
FileOpenPicker p;
p.FileTypeFilter().ReplaceAll({ L".png", L".jpg", L".gif" });
Migración del método CopyButton_ClickPorting the CopyButton_Click method
Iteración en la colecciónIterate over collection foreach (var v in c) for (auto&& v : c) Migración del método BuildClipboardFormatsOutputStringPorting the BuildClipboardFormatsOutputString method
Detección de una excepciónCatch an exception catch (Exception ex) catch (winrt::hresult_error const& ex) Migración del método PasteButton_ClickPorting the PasteButton_Click method
Detalles de la excepciónException details ex.Message ex.message() Migración del método PasteButton_ClickPorting the PasteButton_Click method
Obtención de un valor de propiedadGet a property value myObject.MyProperty myObject.MyProperty() Migración del método NotifyUserPorting the NotifyUser method
Obtención de un valor de propiedadSet a property value myObject.MyProperty = value; myObject.MyProperty(value);
Incremento de un valor de propiedadIncrement a property value myObject.MyProperty += v; myObject.MyProperty(thing.Property() + v);
Para las cadenas, cambie a un generador.For strings, switch to a builder
ToString()ToString() myObject.ToString() winrt::to_hstring(myObject) ToString()ToString()
Cadena de idioma para la cadena Windows RuntimeLanguage string to Windows Runtime string N/AN/A winrt::hstring{ s }
Creación de cadenasString-building StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
String-buildingString-building
Interpolación de cadenasString interpolation $"{i++}) {s.Title}" winrt::to_hstring o winrt::hstring::operator+ winrt::to_hstring, and/or winrt::hstring::operator+ Migración del método OnNavigatedToPorting the OnNavigatedTo method
Cadena vacía para la comparaciónEmpty string for comparison System.String.EmptySystem.String.Empty winrt::hstring::emptywinrt::hstring::empty Migración del método UpdateStatusPorting the UpdateStatus method
Creación de una cadena vacíaCreate empty string var myEmptyString = String.Empty; winrt::hstring myEmptyString{ L"" };
Operaciones de diccionarioDictionary operations 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)
Conversión de tipos (iniciar en caso de error)Type conversion (throw on failure) (MyType)v v.as<MyType>() Migración del método Footer_ClickPorting the Footer_Click method
Conversión de tipos (null en caso de error)Type conversion (null on failure) v as MyType v.try_as<MyType>() Migración del método PasteButton_ClickPorting the PasteButton_Click method
Los elementos XAML con x:Name son propiedades.XAML elements with x:Name are properties MyNamedElement MyNamedElement() Migración del constructor, Current y FEATURE_NAMEPorting the constructor, Current, and FEATURE_NAME
Cambio al subproceso de interfaz de usuarioSwitch to the UI thread CoreDispatcher.RunAsyncCoreDispatcher.RunAsync CoreDispatcher.RunAsync o winrt::resume_foregroundCoreDispatcher.RunAsync, or winrt::resume_foreground Migración del método NotifyUser y Migración del método HistoryAndRoamingPorting the NotifyUser method, and Porting the HistoryAndRoaming method
Construcción de elementos de la interfaz de usuario con código imperativo en una página XAMLUI element construction in imperative code in a XAML page Consulte la construcción de elementos de la interfaz de usuario.See UI element construction Consulte la construcción de elementos de la interfaz de usuario.See UI element construction

En las secciones siguientes se explican con más detalle los elementos de la tabla.The following sections go into more detail regarding some of the items in the table.

Construcción de elementos de la interfaz de usuarioUI element construction

En estos ejemplos de código se muestra la construcción de un elemento de la interfaz de usuario en el código imperativo de una página XAML.These code examples show the construction of a UI element in the imperative code of a XAML page.

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

Los tipos de C# proporcionan el método Object.ToString.C# types provide the Object.ToString method.

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

C++/ WinRT no ofrece directamente esta función, pero puedes elegir alternativas.C++/WinRT doesn't directly provide this facility, but you can turn to alternatives.

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

C++/WinRT también admite winrt::to_hstring para un número limitado de tipos.C++/WinRT also supports winrt::to_hstring for a limited number of types. Tendrás que agregar sobrecargas para los tipos adicionales a los que quieras aplicar stringify.You'll need to add overloads for any additional types you want to stringify.

LanguageLanguage Stringify intStringify int Stringify enumStringify enum
C#C# string result = "hello, " + intValue.ToString();
string result = $"hello, {intValue}";
string result = "status: " + status.ToString();
string result = $"status: {status}";
C++/WinRTC++/WinRT hstring result = L"hello, " + to_hstring(intValue); // must define overload (see below)
hstring result = L"status: " + to_hstring(status);

En caso de aplicar stringify en una enumeración, debes proporcionar la implementación de winrt::to_hstring.In the case of stringifying an enum, you will need to provide the implementation of 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));
        }
    }
}

El enlace de datos suele consumir implícitamente estas aplicaciones de stringify.These stringifications are often consumed implicitly by data binding.

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

Estos enlaces ejecutarán winrt::to_hstring de la propiedad enlazada.These bindings will perform winrt::to_hstring of the bound property. En el caso del segundo ejemplo (StatusEnum), debes proporcionar tu propia sobrecarga de winrt::to_hstring; de lo contrario, recibirás un error del compilador.In the case of the second example (the StatusEnum), you must provide your own overload of winrt::to_hstring, otherwise you'll get a compiler error.

Consulta también el tema Migración del método Footer_Click.Also see Porting the Footer_Click method.

Creación de cadenasString-building

En el caso de la creación de cadenas, C# tiene un tipo StringBuilder integrado.For string building, C# has a built-in StringBuilder type.

CategoryCategory C#C# C++/WinRTC++/WinRT
Creación de cadenasString-building StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Anexo de una cadena de Windows Runtime conservando los valores NULLAppend a Windows Runtime string, preserving nulls builder.Append(s); builder << std::wstring_view{ s };
Adición de una nueva líneaAdd a newline builder.Append(Environment.NewLine); builder << std::endl;
Acceso al resultadoAccess the result s = builder.ToString(); ws = builder.str();

Consulta también los temas Migración del método BuildClipboardFormatsOutputString y Migración del método DisplayChangedFormats.Also see Porting the BuildClipboardFormatsOutputString method, and Porting the DisplayChangedFormats method.

Ejecución de código en el subproceso principal de la interfaz de usuarioRunning code on the main UI thread

Este ejemplo se toma del Ejemplo de escáner de códigos de barras.This example is taken from the Barcode scanner sample.

Cuando quiera trabajar en el subproceso principal de la interfaz de usuario de un proyecto en C#, normalmente usará el método CoreDispatcher.RunAsync, de la siguiente manera.When you want to do work on the main UI thread in a C# project, you typically use the CoreDispatcher.RunAsync method, like this.

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

Es mucho más sencillo expresarlo en C++/WinRT.It's much simpler to express that in C++/WinRT. Observe que aceptamos parámetros por valor suponiendo que queremos acceder a ellos después del primer punto de suspensión (co_await, en este caso).Notice that we're accepting parameters by value on the assumption we'll want to access them after the first suspension point (the co_await, in this case). Para más información, vea Paso de parámetros.For more info, see Parameter-passing.

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

Si necesita hacer el trabajo con una prioridad distinta de la predeterminada, consulte la función winrt::resume_foreground, que tiene una sobrecarga que toma prioridad.If you need to do the work at a priority other than the default, then see the winrt::resume_foreground function, which has an overload that takes a priority. Para ejemplos de código que muestran cómo esperar una llamada a winrt::resume_foreground, vea Programación teniendo en cuenta la afinidad de subprocesos.For code examples showing how to await a call to winrt::resume_foreground, see Programming with thread affinity in mind.

Definición de las clases en tiempo de ejecución en IDLDefine your runtime classes in IDL

Consulta los temas IDL para el tipo de MainPage y Consolidación de los archivos .idl.See IDL for the MainPage type, and Consolidate your .idl files.

Inclusión de los archivos de encabezado de espacio de nombres de Windows de C++/WinRT necesariosInclude the C++/WinRT Windows namespace header files that you need

En C++/WinRT, siempre que quieras usar un tipo desde un espacio de nombres de Windows, debes incluir el archivo de encabezado de espacio de nombres de Windows de C++/WinRT correspondiente.In C++/WinRT, whenever you want to use a type from a Windows namespaces, you need to include the corresponding C++/WinRT Windows namespace header file. Para obtener un ejemplo, consulta el tema Migración del método NotifyUser.For an example, see Porting the NotifyUser method.

Conversiones boxing y unboxingBoxing and unboxing

C# aplica automáticamente boxing a los valores escalares para convertirlos en objetos.C# automatically boxes scalars into objects. C++/WinRT requiere que llames a la función winrt::box_value de manera explícita.C++/WinRT requires you to call the winrt::box_value function explicitly. Ambos lenguajes requieren que apliques la conversión unboxing de manera explícita.Both languages require you to unbox explicitly. Consulta Conversión boxing y unboxing con C++/WinRT.See Boxing and unboxing with C++/WinRT.

En las tablas siguientes, usaremos estas definiciones.In the tables that follows, we'll use these definitions.

C#C# C++/WinRTC++/WinRT
int i; int i;
string s; winrt::hstring s;
object o; IInspectable o;
OperaciónOperation C#C# C++/WinRTC++/WinRT
BoxingBoxing o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
UnboxingUnboxing i = (int)o;
s = (string)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

C++/CX y C# producen excepciones si intentas aplicar unboxing para convertir un puntero nulo en a un tipo de valor.C++/CX and C# raise exceptions if you try to unbox a null pointer to a value type. C++/WinRT considera que se trata de un error de programación y se bloquea.C++/WinRT considers this a programming error, and it crashes. En C++/WinRT, usa la función winrt::unbox_value_or si quieres controlar el caso en el que el objeto no sea del tipo que pensabas que era.In C++/WinRT, use the winrt::unbox_value_or function if you want to handle the case where the object is not of the type that you thought it was.

EscenarioScenario C#C# C++/WinRTC++/WinRT
Conversión unboxing de un entero conocidoUnbox a known integer i = (int)o; i = unbox_value<int>(o);
Si o es nullIf o is null System.NullReferenceException BloqueoCrash
Si o no es un entero con conversión boxingIf o is not a boxed int System.InvalidCastException BloqueoCrash
Unboxing de entero, usar reserva si es null; bloquear en cualquier otro casoUnbox int, use fallback if null; crash if anything else i = o != null ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Unboxing de entero, si es posible; usar reversión en cualquier otro casoUnbox int if possible; use fallback for anything else i = as int? ?? fallback; i = unbox_value_or<int>(o, fallback);

Para obtener un ejemplo, consulta los temas Migración del método OnNavigatedTo y Migración del método Footer_Click.For an example, see Porting the OnNavigatedTo method, and Porting the Footer_Click method.

Conversiones boxing y unboxing en una cadenaBoxing and unboxing a string

En cierto modo, una cadena es un tipo de valor y, de otro modo, un tipo de referencia.A string is in some ways a value type, and in other ways a reference type. C# y C++/WinRT tratan las cadenas de manera diferente.C# and C++/WinRT treat strings differently.

El tipo de ABI HSTRING es un puntero a una cadena de recuento de referencias.The ABI type HSTRING is a pointer to a reference-counted string. Pero no se obtiene de IInspectable, por lo que, técnicamente, no es un objeto.But it doesn't derive from IInspectable, so it's not technically an object. Además, un HSTRING nulo representa la cadena vacía.Furthermore, a null HSTRING represents the empty string. La conversión boxing aplicada a cosas que no se derivan de IInspectable se realiza encapsulándolas dentro de un IReference<T> , y Windows Runtime proporciona una implementación estándar en forma de objeto PropertyValue (los tipos personalizados se notifican como PropertyType::OtherType).Boxing of things not derived from IInspectable is done by wrapping them inside an IReference<T>, and the Windows Runtime provides a standard implementation in the form of the PropertyValue object (custom types are reported as PropertyType::OtherType).

C# representa una cadena de Windows Runtime como tipo de referencia; mientras que C++/WinRT proyecta una cadena como tipo de valor.C# represents a Windows Runtime string as a reference type; while C++/WinRT projects a string as a value type. Es decir, una cadena nula a la que se ha aplicado la conversión boxing puede tener distintas representaciones en función de cómo llegue allí.This means that a boxed null string can have different representations depending how you got there.

ComportamientoBehavior C#C# C++/WinRTC++/WinRT
DeclaracionesDeclarations object o;
string s;
IInspectable o;
hstring s;
Categoría de tipo de cadenaString type category Tipo de referenciaReference type Tipo de valorValue type
HSTRING null se proyecta comonull HSTRING projects as "" hstring{}
¿Son null y "" idénticos?Are null and "" identical? NoNo Yes
Validez de nullValidity of null s = null;
s.Length genera NullReferenceExceptions.Length raises NullReferenceException
s = hstring{};
s.size() == 0 (válido)s.size() == 0 (valid)
Si asignas una cadena null a un objetoIf you assign null string to object o = (string)null;
o == null
o = box_value(hstring{});
o != nullptr
Si asignas "" a un objetoIf you assign "" to object o = "";
o != null
o = box_value(hstring{L""});
o != nullptr

Conversiones boxing y unboxing básicas.Basic boxing and unboxing.

OperaciónOperation C#C# C++/WinRTC++/WinRT
Aplicar boxing a una cadenaBox a string o = s;
Una cadena vacía se convierte en un objeto con un valor distinto de null.Empty string becomes non-null object.
o = box_value(s);
Una cadena vacía se convierte en un objeto con un valor distinto de null.Empty string becomes non-null object.
Unboxing a una cadena conocidaUnbox a known string s = (string)o;
El objeto null se convierte en una cadena null.Null object becomes null string.
InvalidCastException si no es una cadena.InvalidCastException if not a string.
s = unbox_value<hstring>(o);
Un objeto null se bloquea.Null object crashes.
Se bloquea si no es una cadena.Crash if not a string.
Conversión unboxing de una posible cadenaUnbox a possible string s = o as string;
Un objeto null o que no es una cadena se convierte en una cadena null.Null object or non-string becomes null string.

OOR

s = o as string ?? fallback;
Un valor null o que no es una cadena se convierte en un elemento Fallback.Null or non-string becomes fallback.
Una cadena vacía se conserva.Empty string preserved.
s = unbox_value_or<hstring>(o, fallback);
Un valor null o que no es una cadena se convierte en un elemento Fallback.Null or non-string becomes fallback.
Una cadena vacía se conserva.Empty string preserved.

Poner una clase a disposición de la extensión de marcado {Binding}Making a class available to the {Binding} markup extension

Si piensas usar la extensión de marcado {Binding} para enlazar datos al tipo de datos, consulta Objeto de enlace que se declara usando {Binding}.If you intend to use the {Binding} markup extension to data bind to your data type, then see Binding object declared using {Binding}.

Consumir objetos a partir del marcado XAMLConsuming objects from XAML markup

En un proyecto de C#, puedes consumir miembros privados y elementos con nombre del marcado XAML.In a C# project, you can consume private members and named elements from XAML markup. Pero en C++/WinRT, todas las entidades consumidas por la extensión de marcado {x:Bind} de XAML deben exponerse públicamente en IDL.But in C++/WinRT, all entities consumed by using the XAML {x:Bind} markup extension must be exposed publicly in IDL.

Además, el enlace a un valor booleano muestra true o falseen C#, pero muestra Windows.Foundation.IReference`1<Boolean> en C++/WinRT.Also, binding to a Boolean displays true or false in C#, but it shows Windows.Foundation.IReference`1<Boolean> in C++/WinRT.

Para más información y ejemplos de código, consulta Consumir objetos a partir del marcado.For more info, and code examples, see Consuming objects from markup.

Poner un origen de datos a disposición del marcado XAMLMaking a data source available to XAML markup

En C++/WinRT, versión 2.0.190530.8 y posterior, winrt::single_threaded_observable_vector crea un vector observable que admite tanto IObservableVector<T> como IObservableVector<IInspectable> .In C++/WinRT version 2.0.190530.8 and higher, winrt::single_threaded_observable_vector creates an observable vector that supports both IObservableVector<T> and IObservableVector<IInspectable>. Para obtener un ejemplo, consulta el tema Migración de la propiedad Scenarios.For an example, see Porting the Scenarios property.

Puedes crear el archivo Midl (.idl) de este modo (consulta también Factorizar clases en tiempo de ejecución en archivos (.idl).You can author your Midl file (.idl) like this (also see Factoring runtime classes into Midl files (.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; };
    }
}

E implementarlo de este modo.And implement like this.

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

Para obtener más información, consulta los artículos Controles de elementos de XAML; enlazar a una colección C++/WinRT y Colecciones con C++/WinRT.For more info, see XAML items controls; bind to a C++/WinRT collection, and Collections with C++/WinRT.

Poner un origen de datos a disposición del marcado XAML (antes de C++/WinRT 2.0.190530.8)Making a data source available to XAML markup (prior to C++/WinRT 2.0.190530.8)

El enlace de datos XAML requiere que un origen de los elementos implemente IIterable<IInspectable> , así como una de las siguientes combinaciones de interfaces.XAML data binding requires that an items source implements IIterable<IInspectable>, as well as one of the following combinations of interfaces.

  • IObservableVector<IInspectable>IObservableVector<IInspectable>
  • IBindableVector e INotifyCollectionChangedIBindableVector and INotifyCollectionChanged
  • IBindableVector e IBindableObservableVectorIBindableVector and IBindableObservableVector
  • IBindableVector por sí mismo (no responderá a los cambios)IBindableVector by itself (will not respond to changes)
  • IVector<IInspectable>IVector<IInspectable>
  • IBindableIterable (se iterará y guardará los elementos en una colección privada)IBindableIterable (will iterate and save elements into a private collection)

No se puede detectar una interfaz genérica como IVector<T> en tiempo de ejecución.A generic interface such as IVector<T> can't be detected at runtime. Cada IVector<T> tiene un identificador de interfaz diferente (IID), que es una función de T. Cualquier desarrollador puede expandir el conjunto de T de manera arbitraria, por lo que queda claro que el código de enlace XAML nunca puede conocer el conjunto completo al que va a consultar.Each IVector<T> has a different interface identifier (IID), which is a function of T. Any developer can expand the set of T arbitrarily, so clearly the XAML binding code can never know the full set to query for. Esa restricción no supone un problema para C#, ya que cada objeto CLR que implementa IEnumerable<T> implementa IEnumerable de manera automática.That restriction isn't a problem for C# because every CLR object that implements IEnumerable<T> automatically implements IEnumerable. En el nivel de ABI, eso significa que cada objeto que implementa IObservableVector<T> implementa IObservableVector<IInspectable> de manera automática.At the ABI level, that means that every object that implements IObservableVector<T> automatically implements IObservableVector<IInspectable>.

C++/WinRT no ofrece esa garantía.C++/WinRT doesn't offer that guarantee. Si una clase de C++/WinRT en tiempo de ejecución implementa IObservableVector<T> , no podemos suponer que de, algún modo, también se proporciona una implementación de IObservableVector<IInspectable> .If a C++/WinRT runtime class implements IObservableVector<T>, then we can't assume that an implementation of IObservableVector<IInspectable> is somehow also provided.

Por lo tanto, así es como deberá verse el ejemplo anterior.Consequently, here's how the previous example will need to look.

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

Y la implementación.And the implementation.

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

Si tienes que acceder a objetos en m_bookSkus, tendrás que volver a aplicarles QueryInterface en Bookstore::BookSku.If you need to access objects in m_bookSkus, then you'll need to QI them back to 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;
}

Clases derivadasDerived classes

Para derivar a partir de una clase en tiempo de ejecución, la clase base debe admitir la composición.In order to derive from a runtime class, the base class must be composable. C# no requiere que sigas ningún paso en particular para hacer que estas clases admitan composición, pero C++/WinRT sí.C# doesn't require that you take any special steps to make your classes composable, but C++/WinRT does. Usa la palabra clave unsealed para indicar que quieres que la clase se pueda usar como clase base.You use the unsealed keyword to indicate that you want your class to be usable as a base class.

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

En el archivo de encabezado de tu tipo de implementación, tienes que incluir el archivo de encabezado de la clase base antes de incluir el encabezado generado automáticamente para la clase derivada.In the header file for your implementation type, you must include the base class header file before you include the autogenerated header for the derived class. De lo contrario, obtendrás errores como "uso no válido de este tipo como expresión".Otherwise you'll get errors such as "Illegal use of this type as an expression".

// 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 importantesImportant APIs