Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Sugestão
Se você já leu este tópico antes e está retornando a ele com uma tarefa específica em mente, então você pode ir para a seção Localizar conteúdo com base na tarefa que está executando deste tópico.
Este tópico cataloga de forma abrangente os detalhes técnicos envolvidos na transposição do código-fonte de um projeto C# para o seu equivalente em C++/WinRT.
Para obter um estudo de caso sobre a migração de um dos exemplos de aplicação da Plataforma Universal do Windows (UWP), consulte o tópico complementar Migrando o exemplo do Clipboard para C++/WinRT a partir de C#. Você pode ganhar prática e experiência em portabilidade seguindo esse passo a passo e portando a amostra para si mesmo ao longo do processo.
Como se preparar e o que esperar
O estudo de caso Portando o exemplo de área de transferência para C++/WinRT a partir de C# ilustra exemplos dos tipos de decisões de design de software que você tomará ao portar um projeto para C++/WinRT. Portanto, é uma boa ideia se preparar para a portabilidade obtendo uma compreensão sólida de como o código existente funciona. Dessa forma, você terá uma boa visão geral da funcionalidade do aplicativo e da estrutura do código e, em seguida, as decisões que você tomar sempre o levarão adiante e na direção certa.
Em termos de que tipos de alterações de portabilidade esperar, você pode agrupá-las em quatro categorias.
-
Migrar a projeção de linguagem. O Tempo de Execução do Windows (WinRT) é projetado em várias linguagens de programação. Cada uma dessas projeções de linguagem é projetada para parecer idiomática para a linguagem de programação em questão. Para C#, alguns tipos do Tempo de Execução do Windows são projetados como tipos .NET. Assim, por exemplo, você estará traduzindo System.Collections.Generic.IReadOnlyList<T> de volta para Windows.Foundation.Collections.IVectorView<T>. Também em C#, algumas operações do Tempo de Execução do Windows são projetadas como recursos convenientes da linguagem C#. Um exemplo é que em C# você usa a sintaxe do operador
+=para registrar um delegado de manipulação de eventos. Assim, você estará traduzindo recursos de linguagem como esse de volta para a operação fundamental que está sendo executada (registro de evento, neste exemplo). -
Sintaxe da linguagem de porta. Muitas dessas mudanças são simples transformações mecânicas, substituindo um símbolo por outro. Por exemplo, alterar ponto (
.) para ponto e vírgula dupla (::). -
Procedimento de língua portuária. Algumas delas podem ser mudanças simples e repetitivas (como
myObject.MyPropertyamyObject.MyProperty()). Outros 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 C++/WinRT. Certos detalhes do Tempo de Execução do Windows são tratados implicitamente pelo C#, nos bastidores. Esses detalhes são feitos explicitamente em C++/WinRT. Um exemplo é que você usa um arquivo
.idlpara definir suas classes de tempo de execução.
Após o índice baseado em tarefas a seguir, o restante das seções neste tópico são estruturadas de acordo com a taxonomia acima.
Encontre conteúdo com base na tarefa que está a executar
| Tarefa | Conteúdo |
|---|---|
| Autorar um componente do Tempo de Execução do Windows (WRC) | Certas funcionalidades podem ser alcançadas (ou certas APIs chamadas) somente com C++. Poder-se-á integrar essa funcionalidade num WRC C++/WinRT e, em seguida, consumir o WRC de (por exemplo) uma aplicação C#. Consulte componentes da Runtime do Windows com C++/WinRT e se estiver a criar uma classe da Runtime num componente da Runtime do Windows. |
| Portar um método assíncrono | É uma boa ideia que a primeira linha de um método assíncrono em uma classe de tempo de execução C++/WinRT seja auto lifetime = get_strong(); (consulte Acessando com segurança o este ponteiro em uma co-rotina de membro da classe).Portabilidade de Task, consulte Ação assíncrona.Portagem de Conversão de async void, consulte o método Fire-and-forget em . |
| Migrar uma classe | Primeiro, determine se a classe precisa ser uma classe de tempo de execução ou se pode ser uma classe comum. Para ajudá-lo a decidir isso, consulte o início exato de APIs de autor com C++/WinRT. Em seguida, veja as próximas três linhas abaixo. |
| Migrar uma classe de runtime | Uma classe que compartilha funcionalidade fora do aplicativo C++ ou uma classe que é usada na associação de dados XAML. Veja se estiver a criar uma classe de tempo de execução num componente Windows Runtime, ou se estiver a criar uma classe de tempo de execução para ser referenciada na sua interface XAML. Esses links descrevem isso com mais detalhes, mas uma classe de tempo de execução deve ser declarada no IDL. Se seu projeto já contém um arquivo IDL (por exemplo, Project.idl), recomendamos que você declare qualquer nova classe de tempo de execução nesse arquivo. No IDL, declare todos os métodos e membros de dados que serão usados fora do seu aplicativo ou que serão usados em XAML. Depois de atualizar o arquivo IDL, reconstrua e examine os arquivos de stub gerados (.h e .cpp) na pasta Generated Files do seu projeto (No Explorador de Soluções , com o nó do projeto selecionado, certifique-se de que a opção Mostrar Todos os Arquivos esteja ativada). Compare os arquivos de stub com os arquivos já em seu projeto, adicionando arquivos ou adicionando/atualizando assinaturas de função conforme necessário. A sintaxe do arquivo stub está sempre correta, por isso recomendamos que você a use para minimizar os erros de compilação. Depois que os stubs em seu projeto corresponderem aos dos arquivos de stub, você poderá implementá-los portando o código C#. |
| Adaptar uma classe comum | Consulte Se você não estiver criando uma classe de tempo de execução. |
| Autor IDL |
Introdução ao Microsoft Interface Definition Language 3.0 Se você estiver criando uma classe de tempo de execução a ser referenciada em sua interface do usuário XAML Consumindo objetos da marcação XAML Defina suas classes de tempo de execução no IDL |
| Transferir uma coleção |
Coleções com C++/WinRT Disponibilizar uma fonte de dados para marcações XAML Container associativo Acesso de membro do vetor |
| Portar um evento |
Delegado do manipulador de eventos como membro da classe Revogar o delegado do manipulador de eventos |
| Portar um método | De C#: private async void SampleButton_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) { ... }Para o arquivo C++/WinRT .h: fire_and_forget SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&);Para o arquivo C++/WinRT .cpp: fire_and_forget OcrFileImage::SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&) {...} |
| Cadeias de caracteres de porta |
Tratamento de cadeias de caracteres em C++/WinRT ToString Construção de Strings Boxe e unboxing uma corda |
| Conversão de tipo (fundição de tipo) | C#: o.ToString()C++/WinRT: to_hstring(static_cast<int>(o))Veja também ToString. C#: (Value)oC++/WinRT: unbox_value<Value>(o)Lança se o unboxing falhar. Veja também Boxing e unboxing. C#: o as Value? ?? fallbackC++/WinRT: unbox_value_or<Value>(o, fallback)Retorna uma alternativa se o unboxing falhar. Veja também Boxing e unboxing. C#: (Class)oC++/WinRT: o.as<Class>()Lança se a conversão falhar. C#: o as ClassC++/WinRT: o.try_as<Class>()Retorna null se a conversão falhar. |
Mudanças que envolvem a projeção da linguagem
| Categoria | C# | C++/WinRT | Ver também |
|---|---|---|---|
| Objeto sem tipo |
objectou System.Object |
Windows::Foundation::IInspectable | 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() |
|
| Tipo de coleção típico | IList<T>e Adicionar para adicionar um elemento. | IVector<T>e Append para adicionar um elemento. Se você usar um std::vector em qualquer lugar, então push_back para adicionar um elemento. | |
| Tipo de coleção somente leitura | IReadOnlyList<T> | IVectorView<T> | |
| Delegado do manipulador de eventos como membro da classe | myObject.EventName += Handler; |
token = myObject.EventName({ get_weak(), &Class::Handler }); |
Portar o método EnableClipboardContentChangedNotifications |
| Revogar delegado do manipulador de eventos | myObject.EventName -= Handler; |
myObject.EventName(token); |
Portar o método EnableClipboardContentChangedNotifications |
| Contentor associativo | IDiccionário<K, V> | IMap<K, V> | |
| Acesso de membro vetorial | x = v[i];v[i] = x; |
x = v.GetAt(i);v.SetAt(i, x); |
Registrar/revogar um manipulador de eventos
Em C++/WinRT, você tem várias opções sintáticas para registrar/revogar um delegado do manipulador de eventos, conforme descrito em Manipular eventos usando delegados em C++/WinRT. Consulte também o método Porting the EnableClipboardContentChangedNotifications.
Às vezes, por exemplo, quando um destinatário de evento (um objeto que manipula um evento) está prestes a ser destruído, você desejará revogar um manipulador de eventos para que a fonte do evento (o objeto que gera o evento) não chame um objeto destruído. Veja Revogar um delegado registado. Em casos como esse, crie uma variável de membro event_token para seus manipuladores de eventos. Para obter um exemplo, consulte Porting the EnableClipboardContentChangedNotifications método.
Você também pode registrar um manipulador de eventos na marcação XAML.
<Button x:Name="OpenButton" Click="OpenButton_Click" />
Em C#, o seu método OpenButton_Click pode ser privado, e o XAML ainda poderá conectá-lo ao evento ButtonBase.Click gerado por OpenButton.
Em C++/WinRT, seu método OpenButton_Click deve ser público em seu tipo de implementação se você quiser registrá-lo na marcação XAML. Se você registrar um manipulador de eventos somente em 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, pode fazer com que a página XAML de registo seja uma amiga do seu tipo de implementação e OpenButton_Click privada.
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 cenário final é onde o projeto C# que você está portando vincula ao manipulador de eventos da marcação (para obter mais informações sobre esse cenário, consulte Funções em x:Bind).
<Button x:Name="OpenButton" Click="{x:Bind OpenButton_Click}" />
Você pode simplesmente alterar essa marcação para a Click="OpenButton_Click"mais simples. Ou, se preferir, pode manter essa formatação tal como está. Tudo o que você precisa fazer para dar suporte a ele é declarar o manipulador de eventos no IDL.
void OpenButton_Click(Object sender, Windows.UI.Xaml.RoutedEventArgs e);
Observação
Declare a função como void mesmo que você implementá-la como Fire e esqueça.
Alterações que envolvem a sintaxe da linguagem
| Categoria | C# | C++/WinRT | Ver também |
|---|---|---|---|
| Modificadores de acesso | public \<member\> |
public:\<member\> |
Portabilidade do método Button_Click |
| Aceder a 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> ... |
IAsyncOperation interface, simultaneidade e operações assíncronas com C++/WinRT |
| Método Fire-and-forget (implica operação assíncrona) | async void ... |
winrt::fire_and_forget ... |
Portando o método CopyButton_Click, Fire e esqueça |
| Acessar uma constante enumerada | E.Value |
E::Value |
Portar o método DisplayChangedFormats |
| Aguardar cooperativamente | await ... |
co_await ... |
Portabilidade do método CopyButton_Click |
| Coleção de tipos projetados num campo privado | private List<MyRuntimeClass> myRuntimeClasses = new List<MyRuntimeClass>(); |
std::vector<MyNamespace::MyRuntimeClass>m_myRuntimeClasses; |
|
| Construção de 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 espaço de nomes | A.B.T |
A::B::T |
|
| Nulo | null |
nullptr |
Portabilidade do UpdateStatus método |
| Obter um objeto de tipo | typeof(MyType) |
winrt::xaml_typename<MyType>() |
Portabilidade dos cenários de propriedade |
| Declaração de parâmetro para um método | MyType |
MyType const& |
Passagem de parâmetros |
| Declaração de parâmetro para um método assíncrono | MyType |
MyType |
Passagem de parâmetros |
| Chamar um método estático | T.Method() |
T::Method() |
|
| Cordas |
string, ou System.String |
winrt::hstring | Tratamento de cadeias de caracteres em C++/WinRT |
| Literal de cadeia de caracteres | "a string literal" |
L"a string literal" |
Portando o construtor, atual e FEATURE_NAME |
| Tipo inferido (ou deduzido) | var |
auto |
|
| Diretiva de utilização | using A.B.C; |
using namespace A::B::C; |
Portando o construtor, atual e FEATURE_NAME |
| Literal de cadeia de caracteres bruta | @"verbatim string literal" |
LR"(raw string literal)" |
Adaptação do método DisplayToast |
Observação
Se um arquivo de cabeçalho não contiver uma diretiva using namespace para um determinado namespace, você terá que qualificar totalmente todos os nomes de tipo para esse namespace; ou pelo menos qualificá-los o suficiente para que o compilador os encontre. Para obter um exemplo, consulte Porting the DisplayToast method.
Portagem de classes e membros
Você precisará decidir, para cada tipo de C#, se deseja portá-lo para um tipo Windows Runtime ou para uma classe/struct/enumeração C++ regular. Para obter mais informações e exemplos detalhados que ilustram como tomar essas decisões, consulte Adaptar o exemplo da área de transferência para C++/WinRT a partir do C#.
Uma propriedade C# torna-se uma função acessora, uma função mutadora e um membro de dados subjacente. Para obter mais informações e um exemplo, consulte Portando a propriedade IsClipboardContentChangedEnabled.
Para campos não estáticos, torne-os membros de dados do seu tipo de implementação .
Um campo estático C# torna-se um acessador estático C++/WinRT e/ou função mutadora. Para obter mais informações e um exemplo, consulte Portando o construtor, atual e FEATURE_NAME.
Para funções de membro, novamente, você precisará decidir para cada uma se ela pertence ou não ao IDL, ou se é uma função de membro público ou privado do seu tipo de implementação. Para obter mais informações e exemplos de como decidir, consulte IDL para o MainPage digite.
Migração de marcação XAML e arquivos de ativos
No caso de Portando o exemplo de área de transferência para C++/WinRT a partir de C#, pudemos usar mesmo marcação XAML (incluindo recursos) e arquivos de ativos no projeto C# e C++/WinRT. Em alguns casos, serão necessárias edições na marcação para conseguir isso. Consulte Copiar o XAML e os estilos necessários para concluir a portabilidade MainPage.
Alterações que envolvem procedimentos dentro da língua
| Categoria | C# | C++/WinRT | Ver também |
|---|---|---|---|
| Gerenciamento do tempo de vida em um método assíncrono | N/A |
auto lifetime{ get_strong() }; ouauto lifetime = get_strong(); |
Portabilidade do método CopyButton_Click |
| Eliminação | using (var t = v) |
auto t{ v };t.Close(); // or let wrapper destructor do the work |
Portabilidade do método CopyImage |
| Construir objeto | new MyType(args) |
MyType{ args } ouMyType(args) |
Portabilidade dos cenários de propriedade |
| Criar referência não inicializada | MyType myObject; |
MyType myObject{ nullptr }; ouMyType myObject = nullptr; |
Portando o construtor, atual e FEATURE_NAME |
| Construir objeto em variável com args | 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); |
Portagem do método Footer_Click |
| Construir objeto em variável sem args | var myObject = new T(); |
MyType myObject; |
|
| Sintaxe curta para inicialização de objeto | var p = new FileOpenPicker{ViewMode = PickerViewMode.List}; |
FileOpenPicker p;p.ViewMode(PickerViewMode::List); |
|
| Operação de vetor em lote | var p = new FileOpenPicker{FileTypeFilter = { ".png", ".jpg", ".gif" }}; |
FileOpenPicker p;p.FileTypeFilter().ReplaceAll({ L".png", L".jpg", L".gif" }); |
Portabilidade do método CopyButton_Click |
| Iterar sobre coleção | foreach (var v in c) |
for (auto&& v : c) |
|
| Apanhar uma exceção | catch (Exception ex) |
catch (winrt::hresult_error const& ex) |
Portabilidade do método PasteButton_Click |
| Detalhes da exceção | ex.Message |
ex.message() |
Portabilidade do método PasteButton_Click |
| Obter um valor de propriedade | myObject.MyProperty |
myObject.MyProperty() |
Portabilidade do método NotifyUser |
| Definir um valor de propriedade | myObject.MyProperty = value; |
myObject.MyProperty(value); |
|
| Incrementar um valor de propriedade | myObject.MyProperty += v; |
myObject.MyProperty(thing.Property() + v);Para strings, alterne para um construtor |
|
| ToString() | myObject.ToString() |
winrt::to_hstring(myObject) |
ToString() |
| Cadeia de caracteres de língua para cadeia de caracteres da Windows Runtime | N/A | winrt::hstring{ s } |
|
| Construção de cadeias de caracteres | StringBuilder builder;builder.Append(...); |
std::wostringstream builder;builder << ...; |
Construção de Strings |
| Interpolação de cadeias de caracteres | $"{i++}) {s.Title}" |
winrt::to_hstringe/ou winrt::hstring::operator+ | |
| String vazia para comparação | System.String.Empty | winrt::hstring::vazio | Portabilidade do UpdateStatus método |
| Criar cadeia de caracteres vazia | var myEmptyString = String.Empty; |
winrt::hstring myEmptyString{ L"" }; |
|
| Operações de dicionário | map[k] = v; // replaces any existingv = map[k]; // throws if not presentmap.ContainsKey(k) |
map.Insert(k, v); // replaces any existingv = map.Lookup(k); // throws if not presentmap.HasKey(k) |
|
| Conversão de tipo (gera erro em caso de falha) | (MyType)v |
v.as<MyType>() |
Portagem do método Footer_Click |
| Conversão de tipo (nulo em caso de falha) | v as MyType |
v.try_as<MyType>() |
Portabilidade do método PasteButton_Click |
| Elementos XAML com x:Name são propriedades | MyNamedElement |
MyNamedElement() |
Portando o construtor, atual e FEATURE_NAME |
| Alternar para o thread da interface do usuário | CoreDispatcher.RunAsync | CoreDispatcher.RunAsyncou winrt::resume_foreground | Portando o método NotifyUsere Portando o HistoryAndRoaming método |
| Construção de elemento da interface do usuário em código imperativo em uma página XAML | Consulte construção de elementos da interface do usuário | Consulte construção de elementos da interface do usuário |
As seções a seguir entram em mais detalhes sobre alguns dos itens da tabela.
Construção de elementos 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 de 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ê pode recorrer a alternativas.
int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".
C++/WinRT também suporta winrt::to_hstring para um número limitado de tipos. Você precisará adicionar sobrecargas para quaisquer tipos adicionais que deseja stringificar.
| Língua | Converter int para string | Transformar enum em string |
|---|---|---|
| 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 de querer transformar um enum em string, será necessário 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));
}
}
}
Essas stringificações são frequentemente 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 vinculações executarão winrt::to_hstring da propriedade vinculada. No caso do segundo exemplo (o StatusEnum), você deve fornecer sua própria sobrecarga de winrt::to_hstring, caso contrário, você receberá um erro de compilação.
Consulte também Portando o método Footer_Click.
Construção de cadeias de caracteres
Para a construção de cadeias de caracteres, o C# possui um tipo StringBuilder interno
| Categoria | C# | C++/WinRT |
|---|---|---|
| Construção de cadeias de caracteres | StringBuilder builder;builder.Append(...); |
std::wostringstream builder;builder << ...; |
| Acrescentar uma cadeia de caracteres do Runtime do Windows, preservando valores nulos | builder.Append(s); |
builder << std::wstring_view{ s }; |
| Adicionar uma nova linha | builder.Append(Environment.NewLine); |
builder << std::endl; |
| Aceda ao resultado | s = builder.ToString(); |
ws = builder.str(); |
Consulte também Porting the BuildClipboardFormatsOutputString métodoe Porting the DisplayChangedFormats método.
Executando código no thread principal da interface do usuário
Este exemplo é retirado do exemplo de scanner de código de barras .
Quando você deseja trabalhar no thread principal da interface do usuário em um projeto C#, normalmente usa o CoreDispatcher.RunAsync método, como este.
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 suposição de que desejaremos acessá-los após o primeiro ponto de suspensão (o co_await, neste caso). Para obter mais informações, consulte Parameter-passing.
winrt::fire_and_forget Watcher_Added(DeviceWatcher sender, winrt::DeviceInformation args)
{
co_await Dispatcher();
// Do work on the main UI thread here.
}
Se precisar realizar o trabalho com uma prioridade diferente da padrão, consulte a função winrt::resume_foreground, que tem uma sobrecarga que aceita uma prioridade. Para obter exemplos de código mostrando como aguardar uma chamada para winrt::resume_foreground, consulte Programação tendo em mente a afinidade do thread.
Tarefas relacionadas à portabilidade específicas do C++/WinRT
Defina suas classes de tempo de execução no IDL
Consulte IDL para obter o MainPage digitee Consolidar seus arquivos .idl.
Inclua os arquivos de cabeçalho de namespace do Windows C++/WinRT de que você precisa
Em C++/WinRT, sempre que quiser usar um tipo de namespaces do Windows, você precisará incluir o arquivo de cabeçalho de namespace do Windows C++/WinRT correspondente. Para obter um exemplo, consulte Porting the NotifyUser method.
Boxe e desembalagem
O C# encaixota automaticamente escalares em objetos. C++/WinRT requer que você chame a função winrt::box_value explicitamente. Ambos os idiomas exigem que você desencaixote explicitamente. Veja Boxing e unboxing com C++/WinRT.
Nas tabelas a seguir, usaremos essas definições.
| C# | C++/WinRT |
|---|---|
int i; |
int i; |
string s; |
winrt::hstring s; |
object o; |
IInspectable o; |
| Funcionamento | C# | C++/WinRT |
|---|---|---|
| Boxe | 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# geram exceções se tentar desencaixotar um ponteiro nulo para um tipo de valor. C++/WinRT considera isso um erro de programação e falha. Em C++/WinRT, use a função winrt::unbox_value_or se quiser manipular o caso em que o objeto não é do tipo que você pensava que era.
| Cenário | C# | C++/WinRT |
|---|---|---|
| Desencaixotar um número inteiro conhecido | i = (int)o; |
i = unbox_value<int>(o); |
| Se o é nulo | System.NullReferenceException |
Colisão |
| Se o não for um int embalado | System.InvalidCastException |
Colisão |
| Desencaixotar int, usar alternativa se nulo; falhar se outra coisa | i = o != null ? (int)o : fallback; |
i = o ? unbox_value<int>(o) : fallback; |
| Desencaixotar o 'int' caso seja possível; usar 'fallback' para qualquer outra coisa. | i = as int? ?? fallback; |
i = unbox_value_or<int>(o, fallback); |
Para obter um exemplo, consulte Porting the OnNavigatedTo methode Porting the Footer_Click method.
Boxe e unboxing de uma corda
Uma cadeia de caracteres é, de certa forma, um tipo de valor e, de outras maneiras, um tipo de referência. C# e C++/WinRT tratam cadeias de caracteres de forma diferente.
O tipo ABI HSTRING é um ponteiro para uma cadeia de caracteres contada por referência. Mas não deriva de IInspectable, por isso não é tecnicamente um objeto . Além disso, um HSTRING nulo representa a cadeia de caracteres vazia. O encaixotamento de elementos não derivados de IInspectable é feito encapsulando-os em um IReference<T>, e o Windows Runtime fornece uma implementação padrão através do objeto PropertyValue (tipos personalizados são indicados como PropertyType::OtherType).
C# representa uma cadeia de caracteres do Tempo de Execução do Windows como um tipo de referência; enquanto C++/WinRT projeta uma cadeia de caracteres como um tipo de valor. Isso significa que uma cadeia de caracteres nula em caixa pode ter representações diferentes, dependendo de como você chegou lá.
| 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 |
| null HSTRING projeta como | "" |
hstring{} |
São null e "" idênticos? |
Não | Sim |
| Validade de null | s = null;s.Length levanta NullReferenceException |
s = hstring{};s.size() == 0 (válido) |
| Se você atribuir 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 |
Boxe básico e unboxing.
| Funcionamento | C# | C++/WinRT |
|---|---|---|
| Encaixar uma cadeia de caracteres | o = s;A cadeia de caracteres vazia torna-se objeto não nulo. |
o = box_value(s);A cadeia de caracteres vazia torna-se objeto não nulo. |
| Desencaixotar 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. Crash se não for uma cadeia de caracteres. |
| Desencaixotar uma possível cadeia de caracteres | s = o as string;Objeto nulo ou não-string torna-se cadeia de caracteres nula. OU s = o as string ?? fallback;Um valor nulo ou não-string transforma-se num valor de substituição. Cadeia vazia preservada. |
s = unbox_value_or<hstring>(o, fallback);Um valor nulo ou não-string transforma-se num valor de substituição. Cadeia vazia preservada. |
Tornar uma classe disponível para a extensão de marcação {Binding}
Se você pretende usar a extensão de marcação {Binding} para vincular dados ao seu tipo de dados, consulte objeto Binding declarado usando {Binding}.
Consumindo objetos da marcação XAML
Em um projeto C#, você pode consumir membros privados e elementos nomeados da marcação XAML. Mas em C++/WinRT, todas as entidades consumidas usando a extensão de marcação XAML {x:Bind} devem ser expostas publicamente no IDL.
Além disso, a associação a um Boolean exibe true ou false em C#, mas mostra Windows.Foundation.IReference`1<Boolean> em C++/WinRT.
Para obter mais informações e exemplos de código, consulte Consumindo objetos de marcação.
Disponibilizando uma fonte de dados para marcação XAML
Em C++/WinRT versão 2.0.190530.8 ou posterior, winrt::single_threaded_observable_vector cria um vetor observável que suporta ambos IObservableVector<T> e IObservableVector<IInspectable>. Para obter um exemplo, consulte a propriedade Porting the Scenarios.
Você pode criar o seu arquivo Midl (.idl) desta forma (consulte também Fatoração de classes de tempo de execução 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; };
}
}
E implemente assim.
// 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 saber mais, veja controles de itens XAML; vincular a uma coleção C++/WinRTe coleções com C++/WinRT.
Disponibilizando uma fonte de dados para marcação XAML (antes de C++/WinRT 2.0.190530.8)
A associação de dados XAML requer que uma fonte de itens implemente IIterable<IInspectable>, bem como uma das seguintes combinações de interfaces.
- IObservableVector<IInspectable>
- IBindableVector e INotifyCollectionChanged
- IBindableVector e IBindableObservableVector
- IBindableVector por si só (não responderá a alterações)
- IVector<IInspectable>
- IBindableIterable (irá iterar e salvar elementos numa coleção privada)
Uma interface genérica como IVector<T> não pode ser detetada em tempo de execução. Cada IVector<T> tem um identificador de interface diferente (IID), que é uma função de T. Qualquer desenvolvedor pode expandir o conjunto de T arbitrariamente, portanto, claramente, o código de vinculação XAML nunca pode saber o conjunto completo para consultar. Essa restrição não é um problema para C# porque cada objeto CLR que implementa IEnumerable<T> implementa automaticamente IEnumerable. No nível ABI, isso significa que cada objeto que implementa IObservableVector<T> implementa automaticamente IObservableVector<IInspectable>.
C++/WinRT não oferece essa garantia. Se uma classe de tempo de execução C++/WinRT implementa IObservableVector<T>, então não podemos supor que uma implementação de IObservableVector<IInspectable> de alguma forma também seja fornecida.
Consequentemente, aqui está como o exemplo anterior deverá ser.
...
runtimeclass BookstoreViewModel
{
// This is really an observable vector of BookSku.
Windows.Foundation.Collections.IObservableVector<Object> BookSkus{ get; };
}
E 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;
};
...
Se precisares de aceder a objetos em m_bookSkus, então precisarás de convertê-los de volta 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 derivar de uma classe de tempo de execução, a classe base deve ser componível. O C# não exige que você execute nenhuma etapa especial para tornar suas classes compostáveis, mas o C++/WinRT sim. Use a palavra-chave unsealed para indicar que deseja que a sua classe possa ser utilizada como classe base.
unsealed runtimeclass BasePage : Windows.UI.Xaml.Controls.Page
{
...
}
runtimeclass DerivedPage : BasePage
{
...
}
No arquivo de cabeçalho para seu tipo de implementação , você deve incluir o arquivo de cabeçalho da classe base antes de incluir o cabeçalho gerado automaticamente para a classe derivada. Caso contrário, você receberá erros como "Uso ilegal deste tipo como 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>
{
...
}
}