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.
Tip
Se já leu este tema antes e está a regressar a ele com uma tarefa específica em mente, pode saltar para a secção Encontrar conteúdo com base na tarefa que está a realizar .
Este tópico cataloga de forma abrangente os detalhes técnicos envolvidos na portabilidade do código-fonte num projeto C# para o seu equivalente em C++/WinRT.
Para um estudo de caso sobre a portabilidade de um dos exemplos de aplicações da Plataforma Universal do Windows (UWP), veja o tópico complementar Portar o exemplo do Clipboard para C++/WinRT a partir de C#. Pode ganhar prática e experiência em portabilidade seguindo esse guia passo a passo e adaptando o exemplo por si, à medida que progride.
Como preparar-se e o que esperar
O estudo de caso Porting the Clipboard sample do C# para C++/WinRT ilustra exemplos dos tipos de decisões de design de software que tomará ao portar um projeto para C++/WinRT. Por isso, é uma boa ideia preparar-se para a portabilidade, adquirindo uma compreensão sólida de como funciona o código existente. Assim, terá uma boa visão geral da funcionalidade da aplicação e da estrutura do código, e depois as decisões que tomar o levarão sempre para a frente, na direção certa.
Quanto ao tipo de alterações de portabilidade a esperar, pode agrupá-las em quatro categorias.
-
Adapte a projeção de linguagem. O Windows Runtime (WinRT) é projetado para várias linguagens de programação. Cada uma dessas projeções de linguagem foi concebida para parecer idiomática para a linguagem de programação em questão. Para C#, alguns tipos de Windows Runtime são projetados como tipos .NET. Por exemplo, vais traduzir System.Collections.Generic.IReadOnlyList<T> novamente para Windows.Foundation.Collections.IVectorView<T>. Também em C#, algumas operações do Windows Runtime são projetadas como funcionalidades convenientes da linguagem C#. Um exemplo é que em C# usas a
+=sintaxe do operador para registar um delegado de gestão de eventos. Assim, vais traduzir funcionalidades da linguagem como essa de volta para a operação fundamental que está a ser realizada (registo de eventos, neste exemplo). -
Sintaxe da linguagem de porta. Muitas destas alterações são transformações mecânicas simples, substituindo um símbolo por outro. Por exemplo, mudar ponto (
.) para dois pontos (::). -
Procedimento de linguagem do porto. Algumas destas podem ser mudanças simples e repetitivas (como
myObject.MyPropertyparamyObject.MyProperty()). Outros necessitam de alterações mais profundas (por exemplo, portar um procedimento que envolve o uso do System.Text.StringBuilder para um que envolva o uso do std::wostringstream). -
Tarefas relacionadas com portabilidade que são específicas para C++/WinRT. Certos detalhes do Windows Runtime são tratados implicitamente pelo C#, nos bastidores. Esses detalhes são feitos explicitamente em C++/WinRT. Um exemplo é que usas um
.idlficheiro para definir as tuas classes de runtime.
Após o índice baseado em tarefas que se segue, as restantes secções deste tópico são estruturadas de acordo com a taxonomia acima.
Encontre conteúdo com base na tarefa que está a realizar
| Tarefa | Content |
|---|---|
| Criar um componente do Windows Runtime (WRC) | Certas funcionalidades só podem ser alcançadas (ou chamadas certas APIs) com C++. Podes incorporar essa funcionalidade num WRC C++/WinRT e depois consumir o WRC de (por exemplo) uma aplicação C#. Consulte componentes do Windows Runtime com C++/WinRT e Se estiver a criar uma classe de runtime num componente do Windows Runtime. |
| Portar um método assíncrono | É aconselhável que a primeira linha de um método assíncrono numa classe de runtime do C++/WinRT seja auto lifetime = get_strong(); (ver Acesso seguro ao ponteiro this numa corrotina de membro de uma classe).A portar de Task, veja Ação Assíncrona.Portando de Task<T>, ver operação assíncrona.Ao portar de async void, consulte o método "fire-and-forget". |
| Portar uma classe | Primeiro, determine se a classe precisa de ser uma classe em tempo de execução ou se pode ser uma classe comum. Para te ajudar a decidir isso, vê logo o início das APIs de Autor com C++/WinRT. Depois, veja as três linhas seguintes abaixo. |
| Migrar uma classe de tempo de execução | Uma classe que partilha funcionalidades fora da aplicação C++, ou uma classe usada em ligação de dados XAML. Veja se está a criar uma classe de runtime num componente do Windows Runtime, ou se está a criar uma classe de runtime para ser referenciada na sua interface XAML. Esses links descrevem isto com mais detalhe, mas uma classe de runtime deve ser declarada no IDL. Se o seu project já contiver um ficheiro IDL (por exemplo, Project.idl), então recomendamos que declare qualquer nova classe de runtime nesse ficheiro. No IDL, declare quaisquer métodos e membros de dados que sejam usados fora da sua aplicação, ou que sejam usados em XAML. Depois de atualizar o ficheiro IDL, recompile e consulte os ficheiros 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 Mostrar Todos os Ficheiros está ativado). Compare os ficheiros stub com os que já estão no seu projeto, adicionando ficheiros ou adicionando/atualizando assinaturas de funções conforme necessário. A sintaxe dos ficheiros stub está sempre correta, por isso recomendamos que a utilize para minimizar erros de compilação. Quando os stubs do teu projeto coincidem com os dos ficheiros stub, podes implementá-los portando o código C#. |
| Migrar uma classe comum | Consulte Se não estiver a criar uma classe de tempo de execução. |
| Autor IDL |
Introdução à Linguagem de Definição de Interface Microsoft 3.0 Se estiveres a criar uma classe em tempo de execução para ser referenciada na tua interface XAML Consumir objetos a partir da marcação XAML Defina as suas classes de runtime no IDL |
| Migrar uma coleção |
Coleções com C++/WinRT Disponibilizar uma fonte de dados para marcação XAML Contentor associativo Acesso a membros vetoriais |
| Migrar um evento |
Delegado de processamento de eventos como membro da classe Revogar delegado do processador de eventos |
| Portar um método | De C#: private async void SampleButton_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e) { ... }Para o ficheiro C++/WinRT .h : fire_and_forget SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&);Para o ficheiro C++/WinRT .cpp : fire_and_forget OcrFileImage::SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&) {...} |
| Cordas de porta |
Gestão de strings em C++/WinRT ToString Construção de cordas Boxe e desembalagem de uma corda |
| Conversão de tipos (fundição tipográfica) | 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 and unboxing. C#: o as Value? ?? fallbackC++/WinRT: unbox_value_or<Value>(o, fallback)Retorna o valor predefinido se o desencapsulamento falhar. Veja também Boxe 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 nulo se a conversão falhar. |
Alterações que envolvem a projeção da linguagem
| Category | C# | C++/WinRT | Consulte também |
|---|---|---|---|
| Objeto não tipado |
object, ou System.Object |
Windows::Foundation::IInspectable | Portabilidade do método EnableClipboardContentChangedNotifications |
| Espaços de nomes de projeções | using System; |
using namespace Windows::Foundation; |
|
using System.Collections.Generic; |
using namespace Windows::Foundation::Collections; |
||
| Dimensão de uma coleção | collection.Count |
collection.Size() |
Portabilidade do método BuildClipboardFormatsOutputString |
| Tipo típico de coleção | IList<T>, e Add para adicionar um elemento. | IVector<T>, e Add para adicionar um elemento. Se usares std::vector em qualquer lugar, então usa push_back para adicionar um elemento. | |
| Tipo de coleção de apenas leitura | IReadOnlyList<T> | IVectorView<T> | Portabilidade do método BuildClipboardFormatsOutputString |
| Delegado de manipulador de eventos como membro da classe | myObject.EventName += Handler; |
token = myObject.EventName({ get_weak(), &Class::Handler }); |
Portabilidade do método EnableClipboardContentChangedNotifications |
| Revogar delegado do processador de eventos | myObject.EventName -= Handler; |
myObject.EventName(token); |
Portabilidade do método EnableClipboardContentChangedNotifications |
| Contentor associativo | IDictionary<K, V> | IMap<K, V> | |
| Acesso aos membros de um vetor | x = v[i];v[i] = x; |
x = v.GetAt(i);v.SetAt(i, x); |
Registar/revogar um gestor de eventos
Em C++/WinRT, tens várias opções sintáticas para registar/revogar um delegado handler de eventos, conforme descrito em Handle events, usando delegados em C++/WinRT. Veja também como portar o método EnableClipboardContentChangedNotifications.
Por vezes, por exemplo, quando um destinatário de um evento (um objeto que processa um evento) está prestes a ser destruído, vai querer remover um processador de eventos para que a fonte do evento (o objeto que gera o evento) não invoque um objeto destruído. Ver Revogar um delegado registado. Nesses casos, cria uma variável event_token membro para os teus gestores de eventos. Para um exemplo, veja Portar o método EnableClipboardContentChangedNotifications.
Também pode registar um processador de eventos na linguagem de marcação XAML.
<Button x:Name="OpenButton" Click="OpenButton_Click" />
Em C#, o teu método OpenButton_Click pode ser privado, e o XAML ainda poderá ligá-lo ao evento ButtonBase.Click gerado pelo OpenButton.
Em C++/WinRT, o seu método OpenButton_Click deve ser público no seu tipo deimplementação se quiser registar o método em marcação XAML. Se registares um gestor de eventos apenas em código imperativo, então o gestor de eventos não precisa de ser público.
namespace winrt::MyProject::implementation
{
struct MyPage : MyPageT<MyPage>
{
void OpenButton_Click(
winrt::Windows::Foundation::IInspectable const& sender,
winrt::Microsoft::UI::Xaml::RoutedEventArgs const& args);
}
};
Em alternativa, pode tornar a página XAML registada como 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::Microsoft::UI::Xaml::RoutedEventArgs const& args);
}
};
Um último cenário é quando o projeto C# que estás a portar se liga para o gestor de eventos a partir da marcação (para mais contexto sobre esse cenário, vê Funções em x:Bind).
<Button x:Name="OpenButton" Click="{x:Bind OpenButton_Click}" />
Podes simplesmente mudar essa marcação para a mais simples Click="OpenButton_Click". Ou, se preferires, podes manter essa margem como está. Tudo o que tens de fazer para o suportar é declarar o gestor de eventos no IDL.
void OpenButton_Click(Object sender, Microsoft.UI.Xaml.RoutedEventArgs e);
Note
Declara a função como void mesmo que a implementes como Fogo e esqueça.
Alterações que envolvem a sintaxe da linguagem
| Category | C# | C++/WinRT | Consulte também |
|---|---|---|---|
| Modificadores de acesso | public \<member\> |
public:\<member\> |
Portar o método Button_Click |
| Aceder a um membro de dados | this.variable |
this->variable |
|
| Ação assíncrona | async Task ... |
IAsyncAction ... |
interface IAsyncAction, Concorrência e operações assíncronas com C++/WinRT |
| Operação assíncrona | async Task<T> ... |
IAsyncOperation<T> ... |
Interface IAsyncOperation, Concorrência e operações assíncronas com C++/WinRT |
| Método fire-and-forget (implica execução assíncrona) | async void ... |
winrt::fire_and_forget ... |
Migrar o método CopyButton_Click, disparar e esquecer |
| Aceder a uma constante enumerada | E.Value |
E::Value |
Portabilidade do método DisplayChangedFormats |
| Esperar 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 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 espaço de nomes | A.B.T |
A::B::T |
|
| Null | null |
nullptr |
Portabilidade do método UpdateStatus |
| Obter um objeto tipo | typeof(MyType) |
winrt::xaml_typename<MyType>() |
Migrar a propriedade Scenarios |
| Declaração de parâmetros 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 | Gestão de strings em C++/WinRT |
| Literal de cadeia de caracteres | "a string literal" |
L"a string literal" |
Migrar o construtor, Current e FEATURE_NAME |
| Tipo inferido (ou deduzido) | var |
auto |
Portabilidade do método BuildClipboardFormatsOutputString |
| Diretiva de utilização | using A.B.C; |
using namespace A::B::C; |
Migrar o construtor, Current e FEATURE_NAME |
| Verbatim/literal da cadeia bruta | @"verbatim string literal" |
LR"(raw string literal)" |
Portabilidade do método DisplayToast |
Note
Se um ficheiro de cabeçalho não contiver uma using namespace diretiva para um dado namespace, então terá de qualificar totalmente todos os nomes de tipo para esse namespace; ou pelo menos qualificá-los suficientemente para que o compilador os encontre. Para um exemplo, veja Portar o método DisplayToast.
Migração de classes e membros
Terá de decidir, para cada tipo de C#, se o deve migrar para um tipo do Windows Runtime ou para uma classe/estrutura/enumeração normal de C++. Para mais informações e exemplos detalhados que ilustrem como tomar essas decisões, veja Portar o exemplo do Clipboard para C++/WinRT a partir do C#.
Uma propriedade C# torna-se tipicamente uma função de acesso, uma função mutadora e um elemento de dados de apoio. Para mais informações e um exemplo, veja Migrar a propriedade IsClipboardContentChangedEnabled.
Para campos não estáticos, torne-os membros de dados do teu tipo de implementação.
Um campo estático C# torna-se uma função de acesso estática C++/WinRT e/ou uma função modificadora. Para mais informações e um exemplo, veja Porting the constructor, Current e FEATURE_NAME.
Para funções-membro, mais uma vez, terá de decidir, relativamente a cada uma, se deve ou não constar da IDL, ou se é uma função-membro pública ou privada do seu tipo de implementação. Para mais informações e exemplos de como decidir, veja IDL para o tipo MainPage.
Portar marcação XAML e ficheiros de recursos
No caso de portar a amostra do Clipboard para C++/WinRT a partir do C#, conseguimos usar a mesma marcação XAML (incluindo recursos) e ficheiros de assets em todo o projeto C# e C++/WinRT. Em alguns casos, serão necessárias edições na marcação para que isso se consiga. Consulte Copiar o XAML e os estilos necessários para concluir a migração de MainPage.
Alterações que envolvem procedimentos dentro da linguagem
| Category | C# | C++/WinRT | Consulte também |
|---|---|---|---|
| Gestão de vida num 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) |
Migrar a propriedade Cenários |
| Criar referência não inicializada | MyType myObject; |
MyType myObject{ nullptr }; ouMyType myObject = nullptr; |
Portando o construtor, Current 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); |
Portabilidade do método Footer_Click |
| Construir objeto em variável sem args | var myObject = new T(); |
MyType myObject; |
Portabilidade do método BuildClipboardFormatsOutputString |
| Forma abreviada de inicialização de objetos | var p = new FileOpenPicker{ViewMode = PickerViewMode.List}; |
FileOpenPicker p;p.ViewMode(PickerViewMode::List); |
|
| Operação vetorial em massa | 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 a coleção | foreach (var v in c) |
for (auto&& v : c) |
Portabilidade do método BuildClipboardFormatsOutputString |
| Apanhe uma exceção | catch (Exception ex) |
catch (winrt::hresult_error const& ex) |
Portar o método PasteButton_Click |
| Detalhes das exceções | ex.Message |
ex.message() |
Adaptar o método PasteButton_Click |
| Obtenha o valor da propriedade | myObject.MyProperty |
myObject.MyProperty() |
Portabilidade do método NotifyUser |
| Definir o valor de uma propriedade | myObject.MyProperty = value; |
myObject.MyProperty(value); |
|
| Incrementar o valor de uma propriedade | myObject.MyProperty += v; |
myObject.MyProperty(thing.Property() + v);Para cordas, mude para um construtor |
|
| ToString() | myObject.ToString() |
winrt::to_hstring(myObject) |
ToString() |
| Cadeia de idioma para cadeia do Windows Runtime | N/A | winrt::hstring{ s } |
|
| Construção de cordas | StringBuilder builder;builder.Append(...); |
std::wostringstream builder;builder << ...; |
Construção de cordas |
| Interpolação de cadeias de caracteres | $"{i++}) {s.Title}" |
winrt::to_hstring, e/ou winrt::hstring::operator+ | Portando o método OnNavigatedTo |
| Cadeia vazia para comparação | System.String.Empty | winrt::hstring::empty | Portabilidade do método UpdateStatus |
| 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 (lançar em caso de falha) | (MyType)v |
v.as<MyType>() |
Portabilidade do método Footer_Click |
| Conversão de tipo (nulo em caso de falha) | v as MyType |
v.try_as<MyType>() |
Adaptar o método PasteButton_Click |
| Elementos XAML com x:Name são propriedades | MyNamedElement |
MyNamedElement() |
Portando o construtor, Current e FEATURE_NAME |
| Mudar para o tópico UI | CoreDispatcher.RunAsync | DispatcherQueue.TryEnqueue, ou winrt::resume_foreground | Portar o método NotifyUser e portar o método HistoryAndRoaming |
| Construção de elementos UI em código imperativo numa página XAML | Ver construção dos elementos UI | Ver construção dos elementos UI |
As secções seguintes detalham alguns dos itens da tabela.
Construção dos elementos UI
Estes exemplos de código mostram a construção de um elemento UI no código imperativo de uma página XAML.
var myTextBlock = new TextBlock()
{
Text = "Text",
Style = (Microsoft.UI.Xaml.Style)this.Resources["MyTextBlockStyle"]
};
TextBlock myTextBlock;
myTextBlock.Text(L"Text");
myTextBlock.Style(
winrt::unbox_value<Microsoft::UI::Xaml::Style>(
Resources().Lookup(
winrt::box_value(L"MyTextBlockStyle")
)
)
);
ToString()
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 oferece esta funcionalidade diretamente, mas 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. Terás de adicionar sobrecargas para quaisquer tipos adicionais que queiras stringificar.
| Linguagem | Converter int em cadeia de caracteres | Transformar enum 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 de converter um enum em string, terá de 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));
}
}
}
Estas conversões em cadeia de caracteres são frequentemente utilizadas de forma implícita pela associação de dados.
<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>
Estas ligações executarão winrt::to_hstring da propriedade vinculada. No caso do segundo exemplo (o StatusEnum), deve fornecer a sua própria sobrecarga de winrt::to_hstring; caso contrário, irá obter um erro de compilação.
Veja também a portabilidade do método Footer_Click.
Construção de cordas
Para construção de cordas, o C# tem um tipo StringBuilder incorporado.
| Category | C# | C++/WinRT |
|---|---|---|
| Construção de cordas | StringBuilder builder;builder.Append(...); |
std::wostringstream builder;builder << ...; |
| Acrescentar uma cadeia de carateres do Windows Runtime, preservando os carateres 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(); |
Veja também Portar o método BuildClipboardFormatsOutputString e Portar o método DisplayChangedFormats.
Execução de código na thread principal da IU
Este exemplo é retirado da amostra do leitor de códigos de barras.
Quando queres trabalhar na thread principal da interface num projeto C#, normalmente usas o método DispatcherQueue.TryEnqueue (ou o antigo CoreDispatcher.RunAsync no UWP). Aqui está como é o padrão em C#.
private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
{
DispatcherQueue.TryEnqueue(() =>
{
// Do work on the main UI thread here.
});
}
É muito mais simples expressar isso em C++/WinRT. Note que estamos a aceitar parâmetros pelo valor na suposição de que queremos aceder a eles após o primeiro ponto de suspensão (o co_await, neste caso). Para mais informações, veja Passagem de parâmetros.
winrt::fire_and_forget Watcher_Added(DeviceWatcher sender, winrt::DeviceInformation args)
{
co_await DispatcherQueue();
// Do work on the main UI thread here.
}
Se tiver de efetuar o trabalho com uma prioridade que não a predefinida, consulte a função winrt::resume_foreground, que dispõe de uma sobrecarga que aceita um argumento de prioridade. Para obter exemplos de código que mostram como aguardar uma chamada a winrt::resume_foreground, consulte Programação tendo em conta a afinidade de thread.
Tarefas relacionadas com portabilidade específicas para C++/WinRT
Defina as suas classes de runtime no IDL
Veja IDL para o tipo MainPage e Consolide os seus .idl ficheiros.
Inclua os ficheiros de cabeçalho do namespace Windows C++/WinRT que precisa
No C++/WinRT, sempre que quiser utilizar um tipo de um namespace do Windows, tem de incluir o ficheiro de cabeçalho C++/WinRT correspondente ao namespace do Windows. Para um exemplo, veja Portar o método NotifyUser.
Boxe e desembalagem
C# encaixa automaticamente escalares em objetos. O C++/WinRT exige que chames explicitamente a função winrt::box_value . Ambas as línguas exigem que desembales explicitamente. Consulte Boxing e unboxing com C++/WinRT.
Nas tabelas que se seguem, usaremos estas definições.
| C# | C++/WinRT |
|---|---|
int i; |
int i; |
string s; |
winrt::hstring s; |
object o; |
IInspectable o; |
| Operation | 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# criam exceções se tentares abrir um ponteiro nulo para um tipo de valor. O C++/WinRT considera isto um erro de programação, e crasha. Em C++/WinRT, usa a função winrt::unbox_value_or se quiseres lidar com o caso em que o objeto não é do tipo que pensavas.
| Scenario | C# | C++/WinRT |
|---|---|---|
| Desencapsular um número inteiro conhecido | i = (int)o; |
i = unbox_value<int>(o); |
| Se o for nulo | System.NullReferenceException |
Colisão |
| Se o não for um int encapsulado | System.InvalidCastException |
Colisão |
| Desempacotar int, usar alternativa se for nulo; falhar se for qualquer outra coisa | i = o != null ? (int)o : fallback; |
i = o ? unbox_value<int>(o) : fallback; |
| Desempacote int, se possível; use a alternativa para qualquer outro caso | i = as int? ?? fallback; |
i = unbox_value_or<int>(o, fallback); |
Para um exemplo, veja Portar o método OnNavigatedTo e Portar o método Footer_Click.
Boxe e desembalagem de uma corda
Uma cadeia é, de certa forma, um tipo de valor e, noutros aspetos, um tipo de referência. C# e C++/WinRT tratam as cadeias de forma diferente.
O tipo ABI HSTRING é um apontador para uma cadeia contada por referência. Mas não deriva de IInspectable, por isso tecnicamente não é um objeto. Além disso, uma HSTRING nula representa a cadeia vazia. O empacotamento de elementos não derivados de IInspectable faz-se envolvendo-os num IReference<T>, e o Windows Runtime fornece uma implementação padrão sob a forma do objeto PropertyValue (os tipos personalizados são indicados como PropertyType::OtherType).
C# representa uma string do Windows Runtime como tipo de referência; enquanto C++/WinRT projeta uma string como tipo de valor. Isto significa que uma cadeia nula em caixa pode ter representações diferentes dependendo de como lá chegaste.
| Behavior | C# | C++/WinRT |
|---|---|---|
| Declarações | object o;string s; |
IInspectable o;hstring s; |
| Categoria de tipos de cordas | Tipo de referência | Tipo de valor |
| projeta HSTRING nulos como | "" |
hstring{} |
São nulos e "" idênticos? |
No | Yes |
| Validade do nulo | s = null;s.Length levanta NullReferenceException |
s = hstring{};s.size() == 0 (válido) |
| Se atribuires cadeia nula ao objeto | o = (string)null;o == null |
o = box_value(hstring{});o != nullptr |
Se atribuires "" a um objeto |
o = "";o != null |
o = box_value(hstring{L""});o != nullptr |
Boxe básico e desencaixe.
| Operation | C# | C++/WinRT |
|---|---|---|
| Encapsular uma cadeia de caracteres | o = s;A cadeia vazia torna-se objeto não nulo. |
o = box_value(s);A cadeia vazia torna-se objeto não nulo. |
| Desencapsular uma cadeia de caracteres conhecida | s = (string)o;O objeto nulo torna-se cadeia nula. InvalidCastException se não for uma cadeia de caracteres. |
s = unbox_value<hstring>(o);O objeto nulo crasha. Falha se não for uma cadeia de caracteres. |
| Desempacotar uma cadeia de caracteres opcional | s = o as string;Objeto nulo ou valor não textual é convertido em string nula. OR s = o as string ?? fallback;Nulo ou não-string torna-se um recurso. Fio vazio preservado. |
s = unbox_value_or<hstring>(o, fallback);Nulo ou não-string torna-se um recurso. Fio vazio preservado. |
Disponibilizar uma classe para a extensão de marcação {Binding}
Se pretender usar a extensão de marcação {Binding} para estabelecer uma associação de dados ao seu tipo de dados, consulte objeto Binding declarado com {Binding}.
Consumir objetos a partir da marcação XAML
Num projeto C#, pode consumir membros privados e elementos nomeados a partir da marcação XAML. Mas em C++/WinRT, todas as entidades consumidas ao usar a extensão de marcação XAML {x:Bind} devem ser expostas publicamente em IDL.
Além disso, a vinculação a um Boolean apresenta true ou false em C#, mas mostra Windows.Foundation.IReference`1<Boolean> em C++/WinRT.
Para mais informações e exemplos de código, consulte Consumir objetos a partir de marcação.
Disponibilizar 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 tanto IObservableVector<T> como IObservableVector<IInnspectable>. Para um exemplo, veja a propriedade Porting the Scenarios.
Pode redigir o seu ficheiro Midl (.idl) desta forma (veja também Dividir classes de runtime em ficheiros Midl (.idl)).
namespace Bookstore
{
runtimeclass BookSku { ... }
runtimeclass BookstoreViewModel
{
Windows.Foundation.Collections.IObservableVector<BookSku> BookSkus{ get; };
}
runtimeclass MainPage : Microsoft.UI.Xaml.Controls.Page
{
MainPage();
BookstoreViewModel MainViewModel{ get; };
}
}
E 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 mais informações, consulte controlos de itens XAML; vincular a uma coleção C++/WinRT e Coleções com C++/WinRT.
Disponibilizar uma fonte de dados para marcação XAML (antes do C++/WinRT 2.0.190530.8)
A ligação de dados XAML requer que a origem dos itens implemente IIterable<IInspectable>, bem como uma das seguintes combinações de interfaces.
- IObservableVector<IInspectable>
- IBindableVector e INotifyCollectionChanged
- IBindableVector e IBindableObservableVector
- IBindableVector sozinho (não responde a alterações)
- IVector<IInspectable>
- IBindableIterable (irá iterar e guardar elementos numa coleção privada)
Uma interface genérica como a IVector<T> não pode ser detetada em tempo de execução. Cada IVector<T> tem um identificador de interface (IID) diferente, que é uma função de T. Qualquer programador pode expandir o conjunto de T arbitrariamente, pelo que claramente o código de ligação XAML nunca pode saber o conjunto completo a consultar. Essa restrição não é um problema para C# porque todos os objetos CLR que implementam IEnumerable<T> implementam automaticamente IEnumerable. Ao nível da ABI, isso significa que todos os objetos que implementam IObservableVector<T> implementam automaticamente IObservableVector<IInspectable>.
O C++/WinRT não oferece essa garantia. Se uma classe de runtime C++/WinRT implementar IObservableVector<T>, então não podemos assumir que uma implementação de IObservableVector<IInspectable> também seja fornecida de alguma forma.
Consequentemente, aqui está como o exemplo anterior terá de 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, terás de fazer 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 derivar de uma classe de execução, a classe base deve ser componível. C# não exige que tomes medidas especiais para tornar as tuas classes componíveis, mas C++/WinRT sim. Usas a palavra-chave não selada para indicar que queres que a tua classe seja utilizável como classe base.
unsealed runtimeclass BasePage : Microsoft.UI.Xaml.Controls.Page
{
...
}
runtimeclass DerivedPage : BasePage
{
...
}
No ficheiro de cabeçalho do seu tipo de implementação, deve incluir o ficheiro de cabeçalho da classe base antes de incluir o cabeçalho gerado automaticamente para a classe derivada. Caso contrário, vais 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>
{
...
}
}
APIs importantes
Tópicos relacionados
Windows developer