Compartilhar via


Mover do C# para C++/WinRT

Dica

Se você já leu este tópico antes e está retornando a ele com uma tarefa específica em mente, vá para a seção Localizar um conteúdo com base na tarefa que você está executando deste tópico.

Este tópico descreve os detalhes técnicos envolvidos na portabilidade do código-fonte em um projeto C# para o equivalente no C++/WinRT.

Para obter um estudo de caso de portabilidade de um dos exemplos de aplicativo UWP (Plataforma Universal do Windows), confira o tópico complementar Como portar a amostra de área de transferência para o C++/WinRT do C#. Você pode adquirir prática e experiência em portabilidade seguindo o passo a passo e fazendo a portabilidade da amostra por conta própria durante as etapas.

Como se preparar e o que esperar

O estudo de caso Como portar a amostra de área de transferência para o C++/WinRT do C# ilustra exemplos dos tipos de decisões de design de software que você tomará ao portar um projeto para o C++/WinRT. Portanto, é uma boa ideia se preparar para a portabilidade adquirindo uma compreensão substancial de como o código existente funciona. Dessa forma, você obterá uma boa visão geral da funcionalidade do aplicativo e da estrutura do código, e as decisões que você tomará sempre o conduzirão para frente e na direção certa.

Em relação a quais tipos de portabilidade devem ser esperados, é possível agrupá-los em quatro categorias.

  • Portar a projeção de linguagem. O WinRT (Windows Runtime) é projetado em várias linguagens de programação. Cada uma dessas projeções de linguagem foi criada para dar um aspecto idiomático à linguagem de programação em questão. Quanto ao C#, alguns tipos do Windows Runtime foram projetados como tipos .NET. Por exemplo, você converterá System.Collections.Generic.IReadOnlyList<T> novamente em Windows.Foundation.Collections.IVectorView<T>. Também em C#, algumas operações do Windows Runtime foram projetadas como recursos da linguagem C# convenientes. Um exemplo é que, em C#, você usa a sintaxe do operador += para registrar um delegado de processamento de eventos. Portanto, você converterá recursos de linguagem como esse novamente na operação fundamental que está sendo executada (registro de evento, neste exemplo).
  • Portar a sintaxe da linguagem. Muitas dessas alterações são transformações mecânicas simples, com a substituição de um símbolo por outro. Por exemplo, alteração do ponto (.) para dois-pontos (::).
  • Portar o procedimento da linguagem. Algumas dessas podem ser alterações simples e repetitivas (como myObject.MyProperty para myObject.MyProperty()). Outras precisam de alterações mais profundas (por exemplo, portar um procedimento que envolve o uso de System.Text.StringBuilder para um que envolve o uso de std::wostringstream).
  • Tarefas relacionadas à portabilidade que são específicas para o C++/WinRT. Alguns detalhes do Windows Runtime são resolvidos implicitamente pelo C#, nos bastidores. Esses detalhes são realizados explicitamente no C++/WinRT. Um exemplo disso é que você usa um arquivo .idl para definir as classes de runtime.

Após o índice baseado em tarefa a seguir, o restante das seções deste tópico é estruturado de acordo com a taxonomia acima.

Localizar um conteúdo com base na tarefa que você está executando

Tarefa Conteúdo
Criar um WRC (componente do Windows Runtime) Algumas funcionalidades podem ser obtidas (ou algumas APIs chamadas) somente com o C++. Você pode incluir essa funcionalidade em um WRC do C++/WinRT e consumir o WRC em um aplicativo C#, por exemplo. Confira Componentes do Windows Runtime com o C++/WinRT e Se você estiver criando uma classe de runtime em um componente do Windows Runtime.
Portar um método assíncrono É uma boa ideia que a primeira linha de um método assíncrono em uma classe de runtime C++/WinRT seja auto lifetime = get_strong(); (confira Como acessar com segurança o ponteiro this em uma corrotina de membro de classe).

Portabilidade de Task: confira Ação assíncrona.
Portabilidade de Task<T>: confira Operação assíncrona.
Portabilidade de async void: confira Método fire-and-forget.
Portar uma classe Primeiro, determine se a classe precisa ser uma classe de runtime ou se pode ser uma classe comum. Para ajudar você a decidir isso, confira o início de Criar APIs com o C++/WinRT. Em seguida, confira as próximas três linhas abaixo.
Portar uma classe de runtime Uma classe que compartilha a funcionalidade fora do aplicativo C++ ou uma classe usada na vinculação de dados XAML. Confira Se você estiver criando uma classe de runtime em um componente do Windows Runtime ou Se você estiver criando uma classe de runtime para ser referenciada na interface do usuário XAML.

Esses links descrevem isso mais detalhadamente, mas uma classe de runtime precisa ser declarada em IDL. Se o projeto já contiver um arquivo IDL (por exemplo, Project.idl), recomendamos que você declare qualquer nova classe de runtime nesse arquivo. Em IDL, declare os métodos e os membros de dados que serão usados fora do seu aplicativo ou que serão usados em XAML. Depois de atualizar o arquivo IDL, recompile e examine os arquivos stub gerados (.h e .cpp) na pasta Generated Files do projeto (no Gerenciador de Soluções, com o nó do projeto selecionado, verifique se a opção Mostrar Todos os Arquivos está ativada). Compare os arquivos stub com os arquivos já existentes no projeto e adicione arquivos ou adicione/atualize assinaturas de função, conforme necessário. A sintaxe do arquivo stub está sempre correta. Portanto, recomendamos que você a use para minimizar os erros de build. Depois que os stubs do projeto corresponderem aos dos arquivos stub, você poderá implementá-los portando o código C#.
Portar uma classe comum Confira Se você não estiver criando uma classe de runtime.
Criar uma IDL Introdução à linguagem IDL 3.0 da Microsoft
Se você estiver criando uma classe de runtime para ser referenciada em sua interface de usuário XAML
Como consumir objetos por meio da marcação XAML
Definir as classes de runtime na IDL
Portar uma coleção Coleções com C++/WinRT
Como disponibilizar uma fonte de dados para marcação XAML
Contêiner associativo
Acesso de membro de vetor
Portar um evento Delegado do manipulador de eventos como membro de classe
Revogar delegado do manipulador de eventos
Portar um método Do C#: private async void SampleButton_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) { ... }
Para o arquivo .h C++/WinRT: fire_and_forget SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&);
Para o arquivo .cpp C++/WinRT: fire_and_forget OcrFileImage::SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&) {...}
Portar cadeias de caracteres Processamento da cadeia de caracteres em C++/WinRT
ToString
Construção da cadeia de caracteres
Conversão boxing e unboxing de uma cadeia de caracteres
Conversão de tipo C#: o.ToString()
C++/WinRT: to_hstring(static_cast<int>(o))
Confira também ToString.

C#: (Value)o
C++/WinRT: unbox_value<Value>(o)
é gerado em caso de falha na conversão unboxing. Confira também Conversão boxing e unboxing.

C#: o as Value? ?? fallback
C++/WinRT: unbox_value_or<Value>(o, fallback)
Retorna um fallback em caso de falha na conversão unboxing. Confira também Conversão boxing e unboxing.

C#: (Class)o
C++/WinRT: o.as<Class>()
É gerado em caso de falha na conversão.

C#: o as Class
C++/WinRT: o.try_as<Class>()
Retorna nulo em caso de falha na conversão.

Alterações que envolvem a projeção de linguagem

Categoria C# C++/WinRT Veja também
Objeto não tipado object ou System.Object Windows::Foundation::IInspectable Como portar o método EnableClipboardContentChangedNotifications
Namespaces de projeção using System; using namespace Windows::Foundation;
using System.Collections.Generic; using namespace Windows::Foundation::Collections;
Tamanho de uma coleção collection.Count collection.Size() Como portar o método BuildClipboardFormatsOutputString
Tipo de coleção usual IList<T> e Adicionar para adicionar um elemento. IVector<T> e Acrescentar para adicionar um elemento. Se você usar um std::vector em qualquer lugar, em seguida, use push_back para adicionar um elemento.
Tipo de coleção somente leitura IReadOnlyList<T> IVectorView<T> Como portar o método BuildClipboardFormatsOutputString
Delegado do manipulador de eventos como membro de classe myObject.EventName += Handler; token = myObject.EventName({ get_weak(), &Class::Handler }); Como portar o método EnableClipboardContentChangedNotifications
Revogar delegado do manipulador de eventos myObject.EventName -= Handler; myObject.EventName(token); Como portar o método EnableClipboardContentChangedNotifications
Contêiner associativo IDictionary<K, V> IMap<K, V>
Acesso de membro de vetor x = v[i];
v[i] = x;
x = v.GetAt(i);
v.SetAt(i, x);

Registrar/revogar um manipulador de eventos

No C++/WinRT, há várias opções sintáticas para registrar/revogar um delegado de manipulador de eventos, conforme descrito em Processar eventos usando delegados no C++/WinRT. Confira também Como portar o método EnableClipboardContentChangedNotifications.

Às vezes, por exemplo, quando um destinatário de evento (um objeto que processa um evento) está prestes a ser destruído, o ideal é revogar um manipulador de eventos para que a origem do evento (o objeto que gera o evento) não chame um objeto destruído. Veja Revogar um delegado registrado. Em casos como esse, crie uma variável de membro event_token para os manipuladores de eventos. Para obter um exemplo, confira Como portar o método EnableClipboardContentChangedNotifications.

Você também pode registrar um manipulador de eventos na marcação XAML.

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

No C#, o método OpenButton_Click pode ser privado e o XAML ainda poderá conectá-lo ao evento ButtonBase.Click gerado por OpenButton.

No C++/WinRT, o método OpenButton_Click precisará ser público no tipo de implementação se você desejar registrá-lo na marcação XAML. Se você registrar um manipulador de eventos somente no código imperativo, o manipulador de eventos não precisará ser público.

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, você pode tornar o registro da página XAML um amigo do tipo de implementação e o OpenButton_Click privado.

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

Um último cenário é quando o projeto C# que você está portando se associa ao manipulador de eventos da marcação (para obter mais informações sobre esse cenário, confira Funções em x:Bind).

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

Você pode alterar essa marcação para o Click="OpenButton_Click" mais simples. Ou, caso prefira, você pode manter essa marcação como está. Tudo o que você precisa fazer para dar suporte a ela é declarar o manipulador de eventos em IDL.

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

Observação

Declare a função como void mesmo se você implementá-la como Disparar e esquecer.

Alterações que envolvem a sintaxe de linguagem

Categoria C# C++/WinRT Veja também
Modificadores de acesso public \<member\> public:
    \<member\>
Como portar o método Button_Click
Acessar um membro de dados this.variable this->variable  
Ação assíncrona async Task ... IAsyncAction ... Interface IAsyncAction, Simultaneidade e operações assíncronas com C++/WinRT
Operação assíncrona async Task<T> ... IAsyncOperation<T> ... Interface IAsyncOperation, Simultaneidade e operações assíncronas com C++/WinRT
Método do tipo disparar e esquecer (implica uma operação assíncrona) async void ... winrt::fire_and_forget ... Portabilidade do método CopyButton_Click, Disparar e esquecer
Acessar uma constante enumerada E.Value E::Value Como portar o método DisplayChangedFormats
Espera cooperativa await ... co_await ... Como portar o método CopyButton_Click
Coleção de tipos projetados como um campo privado private List<MyRuntimeClass> myRuntimeClasses = new List<MyRuntimeClass>(); std::vector
<MyNamespace::MyRuntimeClass>
m_myRuntimeClasses;
Construção do 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} };
Separador de namespace A.B.T A::B::T
Null null nullptr Como portar o método UpdateStatus
Obter um objeto de tipo typeof(MyType) winrt::xaml_typename<MyType>() Como portar a propriedade Scenarios
Declaração de parâmetro para um método MyType MyType const& Passagem de parâmetro
Declaração de parâmetro para um método assíncrono MyType MyType Passagem de parâmetro
Chamar um método estático T.Method() T::Method()
Cadeias de caracteres string ou System.String winrt::hstring Processamento da cadeia de caracteres em C++/WinRT
Cadeia de caracteres literal "a string literal" L"a string literal" Como portar o construtor, Current e FEATURE_NAME
Tipo inferido (ou deduzido) var auto Como portar o método BuildClipboardFormatsOutputString
Diretiva using using A.B.C; using namespace A::B::C; Como portar o construtor, Current e FEATURE_NAME
Literal de cadeia de caracteres textual/bruta @"verbatim string literal" LR"(raw string literal)" Como portar o método DisplayToast

Observação

Se um arquivo de cabeçalho não contiver uma diretiva using namespace para determinado namespace, você precisará qualificar totalmente todos os nomes de tipos desse namespace ou, pelo menos, qualificá-los suficientemente para que o compilador os encontre. Para obter um exemplo, confira Como portar o método DisplayToast.

Como portar classes e membros

Você precisará decidir, para cada tipo C#, se desejará portá-lo para um tipo do Windows Runtime ou para uma classe/um struct/uma numeração C++ normal. Para obter mais informações e exemplos detalhados que ilustram como tomar essas decisões, confira Como portar a amostra de área de transferência para o C++/WinRT do C#.

Uma propriedade C# normalmente se torna uma função de acessador, uma função de modificador e um membro de dados de backup. Para obter mais informações e um exemplo, confira Como portar a propriedade IsClipboardContentChangedEnabled.

Para campos não estáticos, torne-os membros de dados do tipo de implementação.

Um campo estático C# torna-se um acessador estático e/ou uma função de modificador C++/WinRT. Para obter mais informações e um exemplo, confira Como portar o construtor, Current e FEATURE_NAME.

Para as funções de membro, novamente, você precisará decidir para cada uma delas se pertencem ou não à IDL ou se elas são uma função de membro pública ou privada do tipo de implementação. Para obter mais informações e exemplos de como tomar essa decisão, confira IDL para o tipo MainPage.

Como portar a marcação XAML e os arquivos de ativos

No caso de Como portar a amostra de área de transferência para o C++/WinRT do C#, pudemos usar a mesma marcação XAML (incluindo os recursos) e os arquivos de ativos em todo o projeto C# e C++/WinRT. Em alguns casos, as edições na marcação serão necessárias para conseguir isso. Confira Copiar o XAML e os estilos necessários para concluir a portabilidade de MainPage.

Alterações que envolvem procedimentos dentro da linguagem

Categoria C# C++/WinRT Veja também
Gerenciamento do tempo de vida em um método assíncrono N/D auto lifetime{ get_strong() }; ou
auto lifetime = get_strong();
Como portar o método CopyButton_Click
Descarte using (var t = v) auto t{ v };
t.Close(); // or let wrapper destructor do the work
Como portar o método CopyImage
Objeto de constructo new MyType(args) MyType{ args } ou
MyType(args)
Como portar a propriedade Scenarios
Criar uma referência não inicializada MyType myObject; MyType myObject{ nullptr }; ou
MyType myObject = nullptr;
Como portar o construtor, Current e FEATURE_NAME
Objeto de constructo em variável com argumentos var myObject = new MyType(args); auto myObject{ MyType{ args } }; ou
auto myObject{ MyType(args) }; ou
auto myObject = MyType{ args }; ou
auto myObject = MyType(args); ou
MyType myObject{ args }; ou
MyType myObject(args);
Como portar o método Footer_Click
Objeto de constructo em variável sem argumentos var myObject = new T(); MyType myObject; Como portar o método BuildClipboardFormatsOutputString
Inicialização abreviada de objeto var p = new FileOpenPicker{
    ViewMode = PickerViewMode.List
};
FileOpenPicker p;
p.ViewMode(PickerViewMode::List);
Operação de vetor em massa var p = new FileOpenPicker{
    FileTypeFilter = { ".png", ".jpg", ".gif" }
};
FileOpenPicker p;
p.FileTypeFilter().ReplaceAll({ L".png", L".jpg", L".gif" });
Como portar o método CopyButton_Click
Iterar na coleção foreach (var v in c) for (auto&& v : c) Como portar o método BuildClipboardFormatsOutputString
Capturar uma exceção catch (Exception ex) catch (winrt::hresult_error const& ex) Como portar o método PasteButton_Click
Detalhes da exceção ex.Message ex.message() Como portar o método PasteButton_Click
Obtém um valor da propriedade myObject.MyProperty myObject.MyProperty() Como portar o método NotifyUser
Definir um valor da propriedade myObject.MyProperty = value; myObject.MyProperty(value);
Incrementar um valor da propriedade myObject.MyProperty += v; myObject.MyProperty(thing.Property() + v);
Para cadeias de caracteres, alterne para um construtor
ToString() myObject.ToString() winrt::to_hstring(myObject) ToString()
Cadeia de caracteres de linguagem para a cadeia de caracteres do Windows Runtime N/D winrt::hstring{ s }
Construção da cadeia de caracteres StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Construção da cadeia de caracteres
Interpolação de cadeia de caracteres $"{i++}) {s.Title}" winrt::to_hstring e/ou winrt::hstring::operator+ Como portar o método OnNavigatedTo
Cadeia de caracteres vazia para comparação System.String.Empty winrt::hstring::empty Como portar o método UpdateStatus
Criar uma cadeia de caracteres vazia var myEmptyString = String.Empty; winrt::hstring myEmptyString{ L"" };
Operações de dicionário 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)
Conversão de tipo (geração em caso de falha) (MyType)v v.as<MyType>() Como portar o método Footer_Click
Conversão de tipo (nula em caso de falha) v as MyType v.try_as<MyType>() Como portar o método PasteButton_Click
Elementos XAML com x:Name são propriedades MyNamedElement MyNamedElement() Como portar o construtor, Current e FEATURE_NAME
Alternar para o thread da IU CoreDispatcher.RunAsync CoreDispatcher.RunAsync ou winrt::resume_foreground Como portar o método NotifyUser e Como portar o método HistoryAndRoaming
Construção de elemento da interface do usuário no código imperativo em uma página XAML Veja a construção de elemento da interface do usuário Veja a construção de elemento da interface do usuário

As seções a seguir fornecem mais detalhes em relação a alguns dos itens da tabela.

construção de instrução da interface do usuário

Estes exemplos de código mostram a construção de um elemento da interface do usuário no código imperativo de uma página 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()

Os tipos C# fornecem o método Object.ToString.

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

O C++/ WinRT não fornece diretamente esse recurso, mas você poderá usar alternativas.

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

O C++/WinRT também dá suporte a winrt::to_hstring em um número limitado de tipos. Você precisará adicionar sobrecargas para os tipos adicionais que deseja converter em cadeia de caracteres.

Language Converter int em cadeia de caracteres Converter uma enumeração em cadeia de caracteres
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);

No caso da conversão de uma enumeração em cadeia de caracteres, você precisará fornecer a implementação de 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));
        }
    }
}

Em geral, essas conversões em cadeia de caracteres são consumidas implicitamente pela vinculação de dados.

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

Essas associações executarão winrt::to_hstring da propriedade associada. No caso do segundo exemplo (o StatusEnum), você precisará fornecer sua própria sobrecarga de winrt::to_hstring; caso contrário, obterá um erro do compilador.

Confira também Como portar o método Footer_Click.

Construção da cadeia de caracteres

Para a construção de cadeia de caracteres, o C# tem um tipo StringBuilder interno.

Categoria C# C++/WinRT
Construção da cadeia de caracteres StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Acrescentar uma cadeia de caracteres do Windows Runtime preservando os valores nulos builder.Append(s); builder << std::wstring_view{ s };
Adicionar uma nova linha builder.Append(Environment.NewLine); builder << std::endl;
Acessar o resultado s = builder.ToString(); ws = builder.str();

Confira também Como portar o método BuildClipboardFormatsOutputString e Como portar o método DisplayChangedFormats.

Executar código no thread da IU principal

Este exemplo é obtido do exemplo de scanner de código de barras.

Quando você deseja trabalhar no thread da IU principal em um projeto C#, normalmente você usa o método CoreDispatcher.RunAsync, assim.

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

É muito mais simples expressar isso em C++/WinRT. Observe que estamos aceitando parâmetros por valor na pressuposição que desejaremos acessá-los após o primeiro ponto de suspensão (co_await, nesse caso). Para obter mais informações, consulte Passagem de parâmetros.

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

Se você precisar fazer o trabalho com uma prioridade diferente do padrão, confira a função winrt::resume_foreground, que tem uma sobrecarga que recebe uma prioridade. Para obter exemplos de código que mostram como aguardar uma chamada para winrt::resume_foreground, confira Programando com a afinidade de thread em mente.

Definir as classes de runtime na IDL

Confira IDL para o tipo MainPage e Consolidar os arquivos .idl.

Incluir os arquivos de cabeçalho do namespace C++/WinRT do Windows de que você precisa

No C++/WinRT, sempre que desejar usar um tipo de um namespace do Windows, inclua o arquivo de cabeçalho do namespace C++/WinRT do Windows correspondente. Para obter um exemplo, confira Como portar o método NotifyUser.

Conversão boxing e unboxing

O C# faz a conversão boxing automática de escalares em objetos. O C++/WinRT exige que você chame explicitamente a função winrt::box_value. Ambas as linguagens exigem que você faça a conversão unboxing explicitamente. Confira Conversão boxing e unboxing com o C++/WinRT.

Nas tabelas a seguir, usaremos estas definições.

C# C++/WinRT
int i; int i;
string s; winrt::hstring s;
object o; IInspectable o;
Operação C# C++/WinRT
Conversão boxing o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
Conversão unboxing i = (int)o;
s = (string)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

O C++/CX e o C# gerarão exceções se você tentar fazer a conversão unboxing de um ponteiro nulo em um tipo de valor. O C++/WinRT considera isso um erro de programação e falha. No C++/WinRT, use a função winrt::unbox_value_or se desejar lidar com o caso em que o objeto não é do tipo que você pensou que fosse.

Cenário C# C++/WinRT
Fazer a conversão unboxing de um inteiro conhecido i = (int)o; i = unbox_value<int>(o);
Se o for nulo System.NullReferenceException Falha
Se o não for um int convertido System.InvalidCastException Falha
Fazer a conversão unboxing de int, se for nulo, usar fallback; falhar, em qualquer outra situação i = o != null ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Fazer a conversão unboxing de int, se possível; usar fallback para qualquer outra situação i = as int? ?? fallback; i = unbox_value_or<int>(o, fallback);

Para obter um exemplo, confira Como portar o método OnNavigatedTo e Como portar o método Footer_Click.

Conversão boxing e unboxing de uma cadeia de caracteres

Uma cadeia de caracteres é, de algumas maneiras, um tipo de valor e, de outras, um tipo de referência. O C# e o C++/WinRT tratam as cadeias de caracteres de maneira diferente.

O tipo do ABI HSTRING é um ponteiro para uma cadeia de caracteres de contagem de referências. Mas ele não é derivado de IInspectable, portanto, não é tecnicamente um objeto. Além disso, um HSTRING nulo representa a cadeia de caracteres vazia. A conversão boxing de itens não derivados de IInspectable é feita encapsulando-os dentro de um IReference<T> e o Windows Runtime fornece uma implementação padrão na forma do objeto PropertyValue (os tipos personalizados são relatados como PropertyType::OtherType).

O C# representa uma cadeia de caracteres do Windows Runtime como um tipo de referência, enquanto o C++/WinRT projeta uma cadeia de caracteres como um tipo de valor. Isso significa que uma cadeia de caracteres nula convertida pode ter diferentes representações, dependendo do procedimento adotado.

Comportamento C# C++/WinRT
Declarações object o;
string s;
IInspectable o;
hstring s;
Categoria de tipo de cadeia de caracteres Tipo de referência Tipo de valor
projetos HSTRING nulos como "" hstring{}
São nulos e idênticos ""? Não Sim
Validade de nulo s = null;
s.Length gera NullReferenceException
s = hstring{};
s.size() == 0 (válido)
Se você atribuir uma cadeia de caracteres nula ao objeto o = (string)null;
o == null
o = box_value(hstring{});
o != nullptr
Se você atribuir "" ao objeto o = "";
o != null
o = box_value(hstring{L""});
o != nullptr

Conversões boxing e unboxing básicas.

Operação C# C++/WinRT
Fazer a conversão boxing de uma cadeia de caracteres o = s;
A cadeia de caracteres vazia se torna um objeto não nulo.
o = box_value(s);
A cadeia de caracteres vazia se torna um objeto não nulo.
Fazer a conversão unboxing de uma cadeia de caracteres conhecida s = (string)o;
O objeto nulo torna-se uma cadeia de caracteres nula.
InvalidCastException se não for uma cadeia de caracteres.
s = unbox_value<hstring>(o);
O objeto nulo falha.
Falha se não for uma cadeia de caracteres.
Fazer a conversão unboxing de uma possível cadeia de caracteres s = o as string;
Objeto nulo ou não cadeia de caracteres se torna uma cadeia de caracteres nula.

OU

s = o as string ?? fallback;
Nulo ou não cadeia de caracteres se torna fallback.
Cadeia de caracteres vazia preservada.
s = unbox_value_or<hstring>(o, fallback);
Nulo ou não cadeia de caracteres se torna fallback.
Cadeia de caracteres vazia preservada.

Como disponibilizar uma classe para a extensão de marcação {Binding}

Se você pretende usar a extensão de marcação {Binding} para associar dados ao tipo de dados, confira Objeto de associação declarado usando {Binding}.

Como consumir objetos por meio da marcação XAML

Em um projeto C#, você pode consumir membros privados e elementos nomeados por meio da marcação XAML. Mas no C++/WinRT, todas as entidades consumidas com o uso da extensão de marcação XAML {x:Bind} precisam ser expostas publicamente em IDL.

Além disso, a associação a um booliano exibe true ou false no C#, mas mostra Windows.Foundation.IReference`1<Boolean> no C++/WinRT.

Para obter mais informações e exemplos de código, confira Como consumir objetos para marcação.

Como disponibilizar uma fonte de dados para marcação XAML

No C++/WinRT 2.0.190530.8 ou superior, winrt::single_threaded_observable_vector cria um vetor observável que dá suporte a IObservableVector<T> e a IObservableVector<IInspectable>. Para obter um exemplo, confira Como portar a propriedade Scenarios.

Você pode criar o arquivo MIDL (.idl) desta forma (confira também Como fatorar classes de runtime em arquivos 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; };
    }
}

Depois, implemente-o desta forma.

// 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 obter informações, confira Controles de itens XAML; associação a uma coleção do C++/WinRT e Coleções com C++/WinRT.

Como disponibilizar uma fonte de dados para marcação XAML (antes do C++/WinRT 2.0.190530.8)

A vinculação de dados XAML exige que uma origem de itens implemente IIterable<IInspectable>, bem como uma das combinações de interfaces a seguir.

  • IObservableVector<IInspectable>
  • IBindableVector e INotifyCollectionChanged
  • IBindableVector e IBindableObservableVector
  • IBindableVector por si só (não responderá a alterações)
  • IVector<IInspectable>
  • IBindableIterable (iterará e salvará elementos em uma coleção particular)

Uma interface genérica, como IVector<T>, não pode ser detectada em runtime. Cada IVector<T> tem um IID (identificador de interface) distinto, que é uma função de T. Qualquer desenvolvedor pode expandir o conjunto de T de maneira arbitrária e, portanto, é evidente que o código de associação XAML nunca pode saber o conjunto completo a ser consultado. Essa restrição não é um problema para o C#, porque cada objeto CLR que implementa IEnumerable<T> implementa IEnumerable automaticamente. No nível do ABI, isso significa que cada objeto que implementa IObservableVector<T> implementa IObservableVector<IInspectable> automaticamente.

O C++/WinRT não oferece essa garantia. Se uma classe de runtime do C++/WinRT implementar IObservableVector<T>, não poderemos pressupor que uma implementação de IObservableVector<IInspectable> também seja fornecida de alguma forma.

Consequentemente, veja abaixo como o exemplo anterior precisará ser examinado.

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

Veja também a implementação.

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

Caso você precise acessar objetos em m_bookSkus, precisará executar o QI novamente para 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;
}

Classes derivadas

Para derivação de uma classe de runtime, a classe base precisa ser combinável. O C# não exige que você execute nenhuma etapa especial para tornar suas classes combináveis, ao contrário do C++/WinRT. Use a palavra-chave não selada para indicar que deseja que a classe seja utilizável como uma classe base.

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

Na classe do cabeçalho do tipo de implementação, inclua o arquivo de cabeçalho da classe base antes de incluir o cabeçalho gerado automaticamente da classe derivada. Caso contrário, você receberá erros como "uso ilícito deste tipo como uma expressão".

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

APIs importantes