Introdução à linguagem IDL 3.0 da Microsoft

A Linguagem de Definição de Interface da Microsoft (MIDL) 3.0 é uma sintaxe moderna simplificada para definir Windows Runtime tipos dentro de arquivos IDL (interface definition language) (.idlarquivos). Essa nova sintaxe se sentirá familiar para qualquer pessoa com C, C++, C#e/ou Java. MIDL 3.0 é uma maneira particularmente conveniente de definir classes de runtime C++/WinRT , sendo dramaticamente mais concisa do que as versões anteriores de IDL (reduzindo os designs em dois terços de comprimento e usando padrões razoáveis para reduzir a necessidade de decoração com atributos).

Veja a aparência do MIDL 3.0; este exemplo demonstra a maioria dos elementos de sintaxe de linguagem que você provavelmente usará.

// Photo.idl
namespace PhotoEditor
{
    delegate void RecognitionHandler(Boolean arg); // delegate type, for an event.

    runtimeclass Photo : Windows.UI.Xaml.Data.INotifyPropertyChanged // interface.
    {
        Photo(); // constructors.
        Photo(Windows.Storage.StorageFile imageFile);

        String ImageName{ get; }; // read-only property.
        Single SepiaIntensity; // read-write property.

        Windows.Foundation.IAsyncAction StartRecognitionAsync(); // (asynchronous) method.

        event RecognitionHandler ImageRecognized; // event.
    }
}

Observe que a sintaxe de MIDL 3.0 foi projetada especificamente e exclusivamente para definir tipos. Você usará uma linguagem de programação diferente para implementar esses tipos. Para usar o MIDL 3.0, você precisará do SDK do Windows versão 10.0.17134.0 (Windows 10, versão 1803) (midl.exe versão 8.01.0622 ou posterior, usado com a opção/winrt).

Observação

Consulte também a referência consolidada Windows Runtime (o sistema de tipos Windows Runtime e os arquivos de metadados do Windows).

MIDL 1.0, 2.0 e 3.0

A Linguagem de Definição de Interface (IDL) começou com o sistema DCE/RPC (Ambiente de Computação Distribuída/Chamadas de Procedimento Remoto). O MIDL 1.0 original é DCE/RPC IDL com aprimoramentos para definir interfaces e coclasses COM.

Uma sintaxe MIDL 2.0 atualizada (também conhecida como MIDLRT) foi desenvolvida na Microsoft para declarar Windows Runtime APIs para a plataforma Windows. Se você olhar na pasta %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\winrt do SDK do Windows, verá exemplos de .idl arquivos gravados com a sintaxe MIDL 2.0. Elas são APIs Windows Runtime internas, declaradas em seu formulário de ABI (interface binária de aplicativo). Esses arquivos existem principalmente para uso de ferramentas— você não criará nem consumirá essas APIs nesse formulário (a menos que você esteja escrevendo código de nível muito baixo).

Veja também a Transição para MIDL 3.0 do MIDLRT clássico.

MIDL 3.0 é uma sintaxe muito mais simples e moderna, cuja finalidade é declarar Windows Runtime APIs. E você pode usá-lo em seus projetos, especialmente para definir classes de runtime C++/WinRT . Os cabeçalhos, para uso do C++/WinRT, para as APIs Windows Runtime internas fazem parte do SDK, dentro da pasta%WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt.

Casos de uso para MIDL 3.0

Em geral, todas as APIs Windows Runtime são projetadas para estar disponíveis para todas as projeções de linguagem Windows Runtime. Isso é feito, em parte, optando por passar exclusivamente Windows Runtime tipos de e para Windows Runtime APIs. Embora seja uma decisão de design válida passar uma interface COM bruta de e para uma API Windows Runtime, isso limita os consumidores dessa API Windows Runtime específica a aplicativos C++. A técnica pode ser vista em cenários de interoperação, por exemplo, ao interoperar entre Direct3D e XAML. Como o Direct3D está na imagem, o cenário é necessariamente restrito a aplicativos C++. Portanto, uma API que requer uma interface COM não impõe nenhuma limitação adicional acima do que é inerente. Por exemplo, um aplicativo C++ pode obter um ponteiro de interface IDXGISwapChain e, em seguida, passá-lo para o método ISwapChainPanelNative::SetSwapChain. Um aplicativo C#, por exemplo, não seria capaz de obter um IDXGISwapChain para começar, portanto, ele não seria capaz de usar esse método por esse motivo. Essas exceções relacionadas ao interoperabilidade vivem em cabeçalhos de interoperabilidade, como windows.ui.xaml.media.dxinterop.h.

Se houver recursos ou funcionalidades de um componente COM que você deseja expor a Windows Runtime projeções de linguagem além do C++, você poderá criar um COMPONENTE de Windows Runtime C++ (WRC) que cria e usa diretamente o componente COM (como DirectX, por exemplo), e expõe uma replicação de alguns subconjuntos de seus recursos e funcionalidades na forma de um componente COM (como DirectX, por exemplo) e expõe uma replicação de alguns subconjuntos de seus recursos e funcionalidades na forma de um Windows Runtime superfície de API que usa e retorna apenas tipos de Windows Runtime. Em seguida, você pode consumir esse WRC de um aplicativo escrito em qualquer Windows Runtime projeção de linguagem.

Estrutura de definição e midl.exe de chamada da linha de comando

Os principais conceitos organizacionais em uma definição midl 3.0 são namespaces, tipos e membros. Um arquivo de origem MIDL 3.0 (um .idl arquivo) contém pelo menos um namespace, no qual há tipos e/ou namespaces subordinados. Cada tipo contém zero ou mais membros.

  • Classes, interfaces, estruturas e enumerações são tipos.
  • Métodos, propriedades, eventos e campos são exemplos de membros.

Quando você compila um arquivo de origem MIDL 3.0, o compilador (midl.exe) emite um arquivo de metadados Windows Runtime (normalmente um .winmd arquivo).

// Bookstore.idl
namespace Bookstore
{
    runtimeclass BookSku : Windows.UI.Xaml.Data.INotifyPropertyChanged
    {
        BookSku();
        BookSku(Single price, String authorName, String coverImagePath, String title);

        Single Price;

        String AuthorName{ get; };
        Windows.UI.Xaml.Media.ImageSource CoverImage{ get; };
        String CoverImagePath{ get; };
        String Title{ get; };

        Boolean Equals(BookSku other);
        void ApplyDiscount(Single percentOff);
    }
}

Como o namespace de um tipo Windows Runtime se torna parte do nome do tipo, o exemplo acima define uma classe de runtime chamada Bookstore.BookSku. Não há nenhuma maneira independente de linguagem de expressar BookSku sem também expressar o namespace.

Essa classe implementa a interface Windows.UI.Xaml.Data.INotifyPropertyChanged . E a classe contém vários membros: dois construtores, uma propriedade de leitura-gravação (Price), algumas propriedades somente leitura (AuthorName through Title) e dois métodos, chamados Equals e ApplyDiscount. Observe o uso do tipo Single em vez de flutuar. E essa cadeia de caracteres tem um "S" maiúsculo.

Dica

O Visual Studio oferece a melhor experiência para compilar o MIDL 3.0, por meio da VSIX (Extensão do Visual Studio) do C++/WinRT. Veja o Suporte do Visual Studio para C++/WinRT e o VSIX.

Mas você também pode compilar o MIDL 3.0 na linha de comando. Se o código-fonte deste exemplo for armazenado em um arquivo chamado Bookstore.idl, você poderá emitir o comando abaixo. Se necessário para seu caso, você pode atualizar o número de versão do SDK usado no comando (que é 10.0.17134.0).

midl /winrt /metadata_dir "%WindowsSdkDir%References\10.0.17134.0\windows.foundation.foundationcontract\3.0.0.0" /h "nul" /nomidl /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd" /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.UniversalApiContract\6.0.0.0\Windows.Foundation.UniversalApiContract.winmd" /reference "%WindowsSdkDir%\References\10.0.17134.0\Windows.Networking.Connectivity.WwanContract\2.0.0.0\Windows.Networking.Connectivity.WwanContract.winmd" Bookstore.idl

A midl.exe ferramenta compila o exemplo e produz um arquivo de metadados nomeado Bookstore.winmd (por padrão, o .idl nome do arquivo é usado).

Dica

Se você usar mais de um arquivo IDL (para obter conselhos sobre isso, consulte As classes de runtime factoring em arquivos Midl (.idl)) e, em seguida, mescle todos os arquivos resultantes .winmd em um único arquivo com o mesmo nome que o namespace raiz. Esse arquivo final .winmd será aquele que os consumidores de suas APIs farão referência.

Nesse caso, BookSku é a única classe de runtime no namespace bookstore , portanto, salvamos uma etapa e acabamos de nomear o .idl arquivo para o namespace.

Aliás, você pode usar o where comando para descobrir onde midl.exe está instalado.

where midl

Se você quiser usar os tipos definidos em um .idl arquivo de um arquivo diferente .idl , use a import diretiva. Para obter mais detalhes e um exemplo de código, consulte controles XAML; associar a uma propriedade C++/WinRT. É claro que, se você estiver consumindo um componente interno ou de terceiros, não terá acesso ao .idl arquivo. Por exemplo, talvez você queira consumir a API win2D Windows Runtime para renderização de elementos gráficos 2D no modo imediato. O comando acima usou a opção /reference para fazer referência a um arquivo de metadados Windows Runtime (.winmd). Neste próximo exemplo, usaremos essa opção novamente, imaginando o cenário em que temos Bookstore.winmd, mas não Bookstore.idl.

// MVVMApp.idl
namespace MVVMApp
{
    runtimeclass ViewModel
    {
        ViewModel();
        Bookstore.BookSku BookSku{ get; };
    }
}

Se o código-fonte do exemplo acima for armazenado em um arquivo chamado MVVMApp.idl, você poderá emitir o comando abaixo para fazer referência Bookstore.winmd.

midl /winrt /metadata_dir "%WindowsSdkDir%References\10.0.17134.0\windows.foundation.foundationcontract\3.0.0.0" /h "nul" /nomidl /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd" /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.UniversalApiContract\6.0.0.0\Windows.Foundation.UniversalApiContract.winmd" /reference "%WindowsSdkDir%\References\10.0.17134.0\Windows.Networking.Connectivity.WwanContract\2.0.0.0\Windows.Networking.Connectivity.WwanContract.winmd" /reference Bookstore.winmd MVVMApp.idl

Namespaces

Um namespace é necessário. Ele prefixa o nome de todos os tipos definidos no escopo do bloco de namespace com o nome do namespace. Um namespace também pode conter declarações de namespace subordinadas. O nome dos tipos definidos em um escopo de namespace subordinado tem um prefixo de todos os nomes de namespace que contêm.

Os exemplos a seguir são duas maneiras de declarar a mesma classe Windows.Foundation.Uri (como você pode ver, os períodos separam os níveis de namespaces aninhados).

namespace Windows.Foundation
{
    runtimeclass Uri : IStringable
    {
        ...
    }
}
namespace Windows
{
    namespace Foundation
    {
        runtimeclass Uri : IStringable
        {
            ...
        }
    }
}

Aqui está outro exemplo mostrando que é legal declarar namespaces e seus tipos de forma aninhada.

namespace RootNs.SubNs1
{
    runtimeclass MySubNs1Class
    {
        void DoWork();
    }

    namespace SubNs2
    {
        runtimeclass MySubNs2Class
        {
            void DoWork();
        }
    }
}

Mas é uma prática mais comum fechar o namespace anterior e abrir um novo, assim.

namespace RootNs.SubNs1
{
    runtimeclass MySubNs1Class
    {
        void DoWork();
    }
}

namespace RootNs.SubNs1.SubNs2
{
    runtimeclass MySubNs2Class
    {
        void DoWork();
    }
}

Tipos

Há dois tipos de tipos de dados em MIDL 3.0: tipos de valor e tipos de referência. Uma variável de um tipo de valor contém diretamente seus dados. Uma variável de um tipo de referência armazena uma referência aos seus dados (essa variável também é conhecida como um objeto).

É possível que duas variáveis de tipo de referência referenciem o mesmo objeto. Portanto, uma operação em uma variável afeta o objeto referenciado pela outra variável. Com tipos de valor, cada uma das variáveis tem sua própria cópia dos dados e não é possível que uma operação em um afete a outra.

Os tipos de valor MIDL 3.0 são divididos ainda mais em tipos simples, tipos de enumeração, tipos de struct e tipos anuláveis.

Os tipos de referência midl 3.0 são divididos ainda mais em tipos de classe, tipos de interface e tipos de delegado.

Aqui está uma visão geral do sistema de tipos midl 3.0. Ao contrário das versões anteriores do MIDL, você não pode usar aliases para esses tipos.

Categoria Descrição
Tipos de valor Tipos simples Integral assinada: Int16, Int32, Int64
Integral sem sinal: UInt8, UInt16, UInt32, UInt64
Caracteres Unicode: Char (representa um UTF-16LE; uma unidade de código Unicode de 16 bits)
Cadeias de caracteres Unicode: Cadeia de caracteres
Ponto flutuante IEEE: Único, Duplo
Booliano: Booleano
UUID de 128 bits: Guid
Tipos enum Tipos definidos pelo usuário da enumeração de formulário E {...}
Tipos struct Tipos definidos pelo usuário do struct S {...} do formulário
Tipos anuláveis Extensões de todos os outros tipos de valor com um valor nulo
Tipos de referência Tipos de aula Classe base final de todos os outros tipos: Objeto
Tipos definidos pelo usuário do formulário runtimeclass C {...}
Tipos de interface Tipos definidos pelo usuário da interface do formulário I {...}
Tipos delegados Tipos definidos pelo usuário do representante <do formulário returnType> D(...)

Os sete tipos integrais fornecem suporte para dados sem sinal de 8 bits; e valores de 16 bits, 32 bits e 64 bits em formato assinado ou sem sinal.

Os dois tipos de ponto flutuante, Single e Double, representam dados usando os formatos IEEE 754 de precisão dupla de 32 bits e precisão dupla de 32 bits, respectivamente.

O tipo booliano midl 3.0 representa valores boolianos; outrue.false

Caracteres e cadeias de caracteres em MIDL 3.0 contêm caracteres Unicode. O tipo Char representa uma unidade de código UTF-16LE; e o tipo string representa uma sequência de unidades de código UTF-16LE.

A tabela a seguir resume os tipos numéricos do MIDL 3.0.

Categoria Bits Tipo Intervalo/precisão
Integral assinado 16 Int16 –32,768...32,767
32 Int32 –2,147,483,648...2,147,483,647
64 Int64 –9,223,372,036,854,775,808...9,223,372,036,854,775,807
Integral sem sinal 8 UInt8 0...255
16 UInt16 0...65,535
32 UInt32 0...4,294,967,295
64 UInt64 0...18,446,744,073,709,551,615
Ponto flutuante 32 Single 1,5 × 10-45 a 3,4 × 1038, precisão de 7 dígitos
64 Double 5.0 × 10-324 a 1,7 × 10308, precisão de 15 dígitos

Os arquivos de origem MIDL 3.0 usam definições de tipo para criar novos tipos. Uma definição de tipo especifica o nome e os membros do novo tipo. Essas categorias de tipo MIDL 3.0 são definíveis pelo usuário.

  • tipos de atributo,
  • tipos de struct,
  • tipos de interface,
  • tipos de runtimeclass,
  • tipos de delegado e
  • enumerar tipos.

Um tipo de atributo define um atributo Windows Runtime que pode ser aplicado a outras definições de tipo. Um atributo fornece metadados sobre o tipo ao qual o atributo é aplicado.

Um tipo de struct define uma estrutura de Windows Runtime que contém membros de dados (campos). Structs são tipos de valor e não exigem alocação de heap. Um membro de dados de um tipo de struct deve ser um tipo de valor ou um tipo anulável. Os tipos de struct não dão suporte à herança.

Um tipo de interface define uma interface Windows Runtime, que é um conjunto nomeado de membros da função. Uma interface pode especificar que uma implementação da interface também deve implementar uma ou mais interfaces adicionais (necessárias) especificadas. Cada tipo de interface deriva diretamente da interface IInspectable Windows Runtime.

Um tipo de runtimeclass define uma classe Windows Runtime (classe de runtime). Uma classe de runtime contém membros que podem ser propriedades, métodos e eventos.

Um tipo delegado define um delegado Windows Runtime, que representa uma referência a um método com uma lista de parâmetros específica e um tipo de retorno. Os delegados possibilitam tratar um método como uma entidade que pode ser passada como um parâmetro. Um delegado é semelhante ao conceito de um ponteiro de função encontrado em algumas outras linguagens. Ao contrário dos ponteiros de função, os delegados são orientados a objetos e são type-safe.

Um tipo de enumeração é um tipo distinto com constantes nomeadas. Cada tipo de enumeração tem um tipo subjacente implícito; Int32 ou UInt32. O conjunto de valores de um tipo de enumeração é o mesmo que o conjunto de valores do tipo subjacente.

O MIDL 3.0 dá suporte a três categorias de tipo adicionais.

  • tipos de matriz unidimensional,
  • tipos de valor anuláveis e
  • o tipo object .

Você não precisa declarar uma matriz unidimensional antes de poder usá-la. Em vez disso, os tipos de matriz são construídos seguindo um nome de tipo entre colchetes. Por exemplo, Int32[] é uma matriz unidimensional de Int32.

Da mesma forma, os tipos de valor anuláveis também não precisam ser definidos antes que possam ser usados. Para cada valor não anulável tipo T (exceto Cadeia de caracteres), há um tipo anulável correspondente Windows.Foundation.IReference<T>, que pode conter o valor nulladicional. Por exemplo, Windows.Foundation.IReference<Int32> é um tipo que pode conter qualquer inteiro de 32 bits ou o valor null. Veja também IReference<T>.

Por fim, o MIDL 3.0 dá suporte ao tipo object, que é mapeado para a interface IInspectable Windows Runtime. Os tipos de referência de interface e runtimeclass derivam conceitualmente do tipo Object ; delegado não.

Expressões em um valor enumerado

Com MIDL 3.0, você só pode usar uma expressão na definição do valor das constantes nomeadas de um tipo enumerado; em outras palavras, em um inicializador de enumeração.

Uma expressão é construída a partir de operandos e operadores. Os operadores em uma expressão indicam quais operações aplicar aos operandos. Exemplos de operadores incluem +, -, *, /e new. Exemplos de operandos incluem literais, campos, variáveis locais e expressões.

Quando uma expressão contém vários operadores, a precedência dos operadores controla a ordem na qual os operadores individuais são avaliados. Por exemplo, a expressão x + y * z é avaliada como x + (y * z) porque o operador * tem precedência maior que o operador +. As operações lógicas têm precedência menor do que as operações bit a bit.

A tabela a seguir resume os operadores midl 3.0, listando as categorias de operador em ordem de precedência da mais alta para a mais baixa. Os operadores em uma mesma categoria têm precedência igual.

Categoria Expressão Descrição
Primária x++ Pós-incremento
x-- Pós-decremento
Unário +x Identidade
-X Negação
!x Negação lógica
{1>~<1}x Negação bit a bit
{1>++<1}x Pré-incremento
--x Pré-decremento
Multiplicativo x * y Multiplicação
x / y Divisão
x % y Resto
Aditiva x + y Adição, concatenação de cadeia de caracteres, combinação de delegado
x – y Subtração, remoção de delegado
Shift x << y Shift esquerdo
x >> y Shift direito
AND bit a bit x & y INteger bit a bit AND
XOR bit a bit x ^ y XOR bit a bit inteiro
OR bit a bit x | Y OR bit a bit inteiro
AND lógico x && y AND lógico booliano
OR lógico x || y OR lógico booliano

Classes

Classes (ou classes de runtime) são os mais fundamentais dos tipos midl 3.0. Uma classe é uma definição de uma agregação de métodos, propriedades e eventos em uma única unidade. As classes dão suporte à herança e ao polimorfismo — mecanismos nos quais as classes derivadas podem estender e especializar classes base.

Você define um novo tipo de classe usando uma definição de classe. Uma definição de classe começa com um cabeçalho que especifica a runtimeclass palavra-chave, o nome da classe, a classe base (se fornecido) e as interfaces implementadas pela classe. O cabeçalho é seguido pelo corpo da classe, que consiste em uma lista de declarações de membro escritas entre os delimitadores { e }.

Aqui está uma definição de uma classe simples chamada Área.

runtimeclass Area
{
    Area(Int32 width, Int32 height);

    Int32 Height;
    Int32 Width;

    static Int32 NumberOfAreas { get; };
}

Isso define um novo Windows Runtime classe denominada Area, que tem um construtor que usa dois parâmetros Int32, duas propriedades de leitura/gravação int32 chamadas Height e Width e uma propriedade estática somente leitura chamada NumberOfAreas.

Por padrão, uma runtimeclass é selada e a derivação dela é não permitida. Consulte as classes Base.

Para associar o XAML a um modelo de exibição, a classe de runtime do modelo de exibição precisa ser definida em MIDL. Para obter mais detalhes, confira Controles XAML; associar a uma propriedade C++/WinRT.

Você pode declarar que uma classe não dá suporte a nenhuma instância (e, consequentemente, deve conter apenas membros estáticos) prefixando a definição de classe de runtime com a static palavra-chave. Adicionar um membro não estático à classe causa um erro de compilação.

static runtimeclass Area
{
    static Int32 NumberOfAreas { get; };
}

Uma classe estática é diferente de uma classe vazia. Veja também classes vazias.

Você pode indicar que uma definição de classe está incompleta prefixando a definição de classe de runtime com a partial palavra-chave. Todas as definições de classe parciais encontradas pelo compilador são combinadas em uma única classe de runtime. Esse recurso é principalmente para cenários de criação XAML, em que algumas das classes parciais são geradas por computador.

Modificador Significado
static A classe não tem instâncias. Consequentemente, somente membros estáticos são permitidos.
partial A definição de classe está incompleta.

Consulte Composição e ativação para modificadores avançados.

Modificadores de acesso de membro

Como MIDL 3.0 é uma linguagem de definição para descrever a superfície pública de tipos Windows Runtime, não há necessidade de sintaxe explícita para declarar a acessibilidade pública de um membro. Todos os membros são implicitamente públicos. É por isso que o MIDL 3.0 não requer nem permite a palavra-chave (efetivamente redundante public ).

Classes base

Uma definição de classe pode especificar uma classe base seguindo o nome da classe e os parâmetros de tipo com dois-pontos e o nome da classe base. Omitir uma especificação de classe base é o mesmo que derivar do tipo Objeto (em outras palavras, de IInspectable).

Observação

Suas classes de modelo de exibição , na verdade, qualquer classe de runtime que você definir em seu aplicativo - não precisam derivar de uma classe base.

Qualquer classe de runtime que você definir no aplicativo que deriva de uma classe base é conhecida como uma classe composável . E há restrições sobre as classes combináveis. Para um aplicativo ser aprovado nos testes do Kit de Certificação de Aplicativos Windows usados pelo Visual Studio e pela Microsoft Store para validar os envios (e, portanto, para que o aplicativo seja processado com êxito na Microsoft Store), uma classe combinável deve derivar de uma classe base do Windows. Isso significa que a classe na própria raiz da hierarquia de herança deve ser um tipo que origina em um namespace Windows.*.

Para obter mais detalhes, confira Controles XAML; associar a uma propriedade C++/WinRT.

No próximo exemplo, a classe base de Volume é Área e a classe base da Área é Windows.UI.Xaml.DependencyObject.

unsealed runtimeclass Area : Windows.UI.Xaml.DependencyObject
{
    Area(Int32 width, Int32 height);
    Int32 Height;
    Int32 Width;
}

runtimeclass Volume : Area
{
    Volume(Int32 width, Int32 height, Int32 depth);
    Int32 Depth;
}

Observação

Aqui, Área e Volume são definidos no mesmo arquivo de origem. Para uma discussão sobre os prós e contras, consulte As classes de runtime de fatoração em arquivos midl (.idl).

Uma classe herda os membros de sua classe base. Herança significa que uma classe contém implicitamente todos os membros de sua classe base, exceto os construtores da classe base. Uma classe derivada pode adicionar novos membros aos que ela herda, mas ela não pode remover a definição de um membro herdado.

No exemplo anterior, Volume herda as propriedades De Altura e Largura da Área. Portanto, cada instância de volume contém três propriedades: Altura, Largura e Profundidade.

Em geral, as regras de resolução de tipo exigem que um nome de tipo seja totalmente qualificado quando referenciado. Uma exceção é quando o tipo foi definido no mesmo namespace que o tipo atual. O exemplo acima funciona como escrito se Área e Volume estiverem no mesmo namespace.

Interfaces implementadas

Uma definição de classe também pode especificar uma lista de interfaces que a classe implementa. Especifique as interfaces como uma lista separada por vírgulas de interfaces seguindo a classe base (opcional).

No exemplo a seguir, a classe Área implementa a interface IStringable ; e a classe Volume implementa o IStringable e a interface IEquatable hipotética .

unsealed runtimeclass Area : Windows.Foundation.IStringable
{
    Area(Int32 width, Int32 height);
    Int32 Height;
    Int32 Width;
}

runtimeclass Volume : Area, Windows.Foundation.IStringable, IEquatable
{
    Volume(Int32 width, Int32 height, Int32 depth);
    Int32 Depth;
}

No MIDL, você não declara os membros da interface na classe. É claro que você precisa declarar e defini-los sobre a implementação real.

Membros

Os membros de uma classe são membros estáticos ou membros de instância. Um membro estático pertence a uma classe. Um membro de instância pertence a um objeto (ou seja, uma instância de uma classe).

Esta tabela mostra os tipos de membro que uma classe pode conter.

Tipo de membro Descrição
Construtores Ações necessárias para inicializar uma instância da classe ou inicializar a própria classe
Propriedades Ações associadas à leitura e gravação de propriedades nomeadas de uma instância da classe ou da própria classe
Métodos Cálculos e ações que podem ser executadas por uma instância da classe ou pela própria classe
Eventos Notificações que podem ser geradas por uma instância da classe

Construtores

O MIDL 3.0 dá suporte à declaração de construtores de instância. Um construtor de instância é um método que implementa as ações necessárias para inicializar uma instância de uma classe. Construtores podem não ser estáticos.

Um construtor é declarado como um método de instância (mas sem tipo de retorno) e com o mesmo nome da classe que contém.

Construtores de instância podem ser sobrecarregados. Por exemplo, a classe Test abaixo declara três construtores de instância; um sem parâmetros (o construtor padrão ), um que usa um parâmetro Int32 e outro que usa dois parâmetros duplos (construtores parametrizados ).

runtimeclass Test
{
    Test();
    Test(Int32 x);
    Test(Double x, Double y);
}

Para obter detalhes sobre a sintaxe das listas de parâmetros, consulte Métodos abaixo.

Propriedades, métodos e eventos de instância são herdados. Construtores de instância não são herdados (com uma exceção) e uma classe não tem construtores de instância diferentes daqueles realmente declarados na classe. Se nenhum construtor de instância for fornecido para uma classe, você não poderá instanciar diretamente a classe. Para essa classe, você normalmente teria um método de fábrica em outro lugar que retorna uma instância da classe.

A exceção são classes não seladas. Uma classe não selada pode ter um ou mais construtores protegidos.

Propriedades

As propriedades são conceitualmente semelhantes aos campos (por exemplo, campos C#; ou os campos de um struct MIDL 3.0). Propriedades e campos são membros com um nome e um tipo associado. No entanto, ao contrário dos campos, as propriedades não denotam locais de armazenamento. Em vez disso, as propriedades têm acessadores que especificam a função a ser executada quando você lê ou grava uma propriedade.

Uma propriedade é declarada como um campo de struct, exceto que a declaração termina com uma get palavra-chave e/ou uma set palavra-chave escrita entre os delimitadores { e }, e terminando em um ponto e vírgula.

Uma propriedade que tem uma get palavra-chave e uma setpalavra-chave é uma propriedade de leitura-gravação. Uma propriedade que tem apenas uma getpalavra-chave é uma propriedade somente leitura. O Windows Runtime não dá suporte a propriedades somente gravação.

Por exemplo, a área de classe, vista anteriormente, contém duas propriedades de leitura/gravação chamadas Height e Width.

unsealed runtimeclass Area
{
    Int32 Height { get; set; };
    Int32 Width; // get and set are implied if both are omitted.
}

A declaração de Width omite as chaves e as get palavras-chave.set A omissão implica que a propriedade é leitura-gravação e é semanticamente idêntica ao fornecimento das get palavras-chave e set dessa ordem,get seguida por set.

Além disso, você pode especificar apenas a get palavra-chave para indicar que a propriedade é somente leitura.

// Read-only instance property returning mutable collection.
Windows.Foundation.Collections.IVector<Windows.UI.Color> Colors { get; };

O Windows Runtime não dá suporte a propriedades somente gravação. Mas você pode especificar apenas a set palavra-chave para revisar uma propriedade somente leitura existente em uma propriedade de leitura-gravação. Veja esta versão do Area como exemplo.

unsealed runtimeclass Area
{
    ...
    Color SurfaceColor { get; };
}

Se você quiser, posteriormente, fazer a leitura-gravação da propriedade SurfaceColor e não precisar manter a compatibilidade binária com definições anteriores de Área (por exemplo, a classe Área é um tipo em um aplicativo que você recompila cada vez), então você pode simplesmente adicionar a set palavra-chave à declaração existente do SurfaceColor como esta.

unsealed runtimeclass Area
{
    ...
    Color SurfaceColor { get; set; };
}

Se, por outro lado , você precisar de estabilidade binária (por exemplo, a classe Área é um componente em uma biblioteca que você envia aos clientes), então você não pode adicionar a set palavra-chave à declaração de propriedade existente. Isso altera a interface binária para sua classe.

Nesse caso, adicione a palavra-chave de propriedade set a uma definição adicional da propriedade no final da classe como esta.

unsealed runtimeclass Area
{
    ...
    Color SurfaceColor { get; };
    ...
    Color SurfaceColor { set; };
}

O compilador produz um erro para uma propriedade somente gravação. Mas não é isso que está sendo feito aqui. Devido à declaração anterior da propriedade como somente leitura, a adição da palavra-chave definida não declara uma propriedade somente gravação, mas sim uma propriedade de leitura-gravação.

A Windows Runtime implementação de uma propriedade é um ou dois métodos de acessador em uma interface. A ordem das palavras-chave get e set na declaração de propriedade determina a ordem dos métodos get e set accessor na interface de suporte.

O get acessador corresponde a um método sem parâmetros com um valor retornado do tipo de propriedade— o getter da propriedade.

Um set acessador corresponde a um método com um único parâmetro chamado valor e nenhum tipo de retorno– o setter de propriedade.

Consequentemente, essas duas declarações produzem interfaces binárias diferentes.

Color SurfaceColor { get; set; };
Color SurfaceColor { set; get; };
Propriedades estáticas e de instância

Semelhante aos métodos, o MIDL 3.0 dá suporte a propriedades de instância e propriedades estáticas. As propriedades estáticas são declaradas com o static modificador prefixado e as propriedades da instância são declaradas sem ele.

Métodos

Um método é um membro que implementa uma computação ou ação que pode ser executada por uma instância da classe ou pela própria classe. Um método estático é acessado por meio da classe. Um método de instância é acessado por meio de uma instância da classe.

Um método tem uma lista (possivelmente vazia) de parâmetros, que representam valores ou referências variáveis passadas para o método. Um método também tem um tipo de retorno, que especifica o tipo do valor calculado e retornado pelo método. O tipo de retorno de um método será void se ele não retornar um valor.

// Instance method with no return value.
void AddData(String data);

// Instance method *with* a return value.
Int32 GetDataSize();

// Instance method accepting/returning a runtime class.
// Notice that you don't say "&" nor "*" for reference types.
BasicClass MergeWith(BasicClass other);

// Asynchronous instance methods.
Windows.Foundation.IAsyncAction UpdateAsync();
Windows.Foundation.IAsyncOperation<Boolean> TrySaveAsync();

// Instance method that returns a value through a parameter.
Boolean TryParseInt16(String input, out Int16 value);

// Instance method that receives a reference to a value type.
Double CalculateArea(ref const Windows.Foundation.Rect value);

// Instance method accepting or returning a conformant array.
void SetBytes(UInt8[] bytes);
UInt8[] GetBytes();

// instance method that writes to a caller-provided conformant array
void ReadBytes(ref UInt8[] bytes);

A assinatura de um método deve ser exclusiva na classe na qual o método é declarado. A assinatura de um método consiste no nome do método, nos tipos de seus parâmetros e/ou no número de seus parâmetros. A assinatura de um método não inclui o tipo de retorno.

Modificadores de visibilidade do método

Um método pode ter um dos dois modificadores de visibilidade opcionais quando o método está presente em uma classe derivada.

O modificador substituível afirma que esse método pode ser substituído por um método (com o mesmo nome e assinatura) pertencente a uma subclasse.

O modificador protegido afirma que esse método só é acessível por membros em uma classe derivada posteriormente.

Sobrecarga de método

A sobrecarga de métodos permite que vários métodos na mesma classe tenham o mesmo nome, desde que seus parâmetros diferem em número (em outras palavras, os métodos têm aridade diferente).

runtimeclass Test
{
    static void F();
    static void F(Double x);
    static void F(Double x, Double y);
}

Observação

Todos os métodos com o mesmo nome devem ter uma aridade diferente. Isso ocorre porque as linguagens de programação com tipo fraco não dão suporte à sobrecarga por tipo.

Parâmetros

Os parâmetros são usados para passar valores ou referências variáveis para um método. Um parâmetro descreve um slot com um tipo e um nome e, opcionalmente, uma palavra-chave do modificador. Um argumento é um valor real passado nesse slot do chamador do método para o chamador.

Os parâmetros de um método obtêm seu valor do argumento específico especificado quando o método é invocado. A forma como os argumentos são passados entre o chamador e o chamador depende do tipo do parâmetro. Por padrão, todos os parâmetros são parâmetros de entrada, ou seja, eles são marshaled do chamador somente para o chamador. As palavras-chave do modificador e out podem ser adicionadas refref constpara modificar a direção padrão do marshaling entre o chamador e o receptor e criar parâmetros de saída. No entanto, nem todas as palavras-chave são válidas com todos os tipos de parâmetro; as combinações válidas são detalhadas abaixo.

Importante

O CLR (Common Language Runtime) tem conceitos e palavras-chave modificadoras que podem parecer semelhantes aos descritos nesta seção. No entanto, na prática, eles não estão relacionados e o efeito desses modificadores é específico para o design e o funcionamento do Windows Runtime.

Os tipos de valor são parâmetros de entrada implicitamente e, por padrão, uma cópia do argumento é passada do chamador para o chamador. Os parâmetros de valor podem ser transformados em parâmetros de saída com a out palavra-chave; nesse caso, o argumento é marshaled do receptor de volta apenas para o chamador.

runtimeclass Test
{
    static void Divide(Int32 x, Int32 y, out Int32 result, out Int32 remainder);
}

Como uma otimização de desempenho especial, os tipos de struct (e nenhum outro tipo), que normalmente são passados por valor como uma cópia completa, podem ser feitos para serem passados por ponteiro para o struct imutável. Isso é obtido com a ref const palavra-chave (nãoconst ref), que marca o parâmetro struct como um parâmetro de entrada, mas instrui o marshaler a passar um ponteiro para o armazenamento do struct, em vez de passar uma cópia completa do struct. No entanto, observe que o struct é imutável; o ponteiro é conceitualmente um ponteiro const. Não há boxe envolvido. Essa é uma opção prática ao aceitar um valor tão grande quanto um Matrix4x4, por exemplo.

runtimeclass Test
{
    static Boolean IsIdentity(ref const Windows.Foundation.Numerics.Matrix4x4 m);
}

Os tipos de referência também são parâmetros de entrada implicitamente, o que significa que o chamador é responsável por alocar o objeto e passar uma referência a ele como argumento; no entanto, como o argumento é uma referência ao objeto, as modificações nesse objeto pelo receptor são observadas pelo chamador após a chamada. Como alternativa, um tipo de referência pode ser feito um parâmetro de saída com a out palavra-chave. Nesse caso, as funções são invertidas; o chamador é aquele que aloca o objeto e o retorna para o chamador. Novamente, as ref palavras-chave não podem ser usadas em geral com tipos de referência (consulte exceção abaixo).

runtimeclass Test
{
    static void CreateObjectWithConfig(Config config, out MyClass newObject);
}

A tabela a seguir resume o comportamento das palavras-chave de marshaling para parâmetros de valor e parâmetros de referência:

Comportamento Alocado por Palavra-chave Tipos Comentários
Parâmetro de entrada Chamador (nenhum) Todos os tipos Comportamento padrão
ref const Somente struct Otimização do desempenho
Parâmetro de saída Receptor out Todos os tipos

Windows Runtime dá suporte a tipos de matriz, cujo comportamento como parâmetro é um pouco diferente. Uma matriz é uma estrutura de dados que contém várias variáveis armazenadas sequencialmente e acessadas por meio de um índice. As variáveis contidas em uma matriz, também chamadas de elementos da matriz, são todas do mesmo tipo e esse tipo é chamado de tipo de elemento da matriz.

O MIDL 3.0 dá suporte a declarações de uma matriz unidimensional.

Um parâmetro de matriz é um tipo de referência e, como todos os tipos de referência, é por padrão um parâmetro de entrada. Nesse caso, o chamador aloca a matriz para o chamador, que pode ler seus elementos, mas não pode modificá-los (somente leitura). Isso é chamado de padrão de matriz de passagem . Como alternativa, o padrão de matriz de preenchimento pode ser usado adicionando a ref palavra-chave ao parâmetro; nessa configuração, a matriz ainda é alocada pelo chamador, mas é conceitualmente um parâmetro de saída no sentido de que o receptor preencherá os valores dos elementos da matriz. Por fim, o último padrão é a matriz de recebimento em que (como todos os parâmetros de referência de saída) o chamador está alocando e inicializando o argumento antes de retornar ao chamador.

runtimeclass Test
{
    // Pass array pattern: read-only array from caller to callee
    void PassArray(Int32[] values);

    // Fill array pattern: caller allocates array for callee to fill
    void FillArray(ref Int32[] values);

    // Receive array pattern: callee allocates and fill an array returned to caller
    void ReceiveArray(out Int32[] values);
}

A tabela a seguir resume o comportamento de matrizes e seus elementos:

Padrão de matriz Palavra-chave Alocado por Acesso de elementos por callee
"Matriz de passagem" (nenhum) Chamador Somente leitura
"Matriz de preenchimento" ref Chamador Somente gravação
"Matriz de recebimento" out Receptor Leitura-gravação

Para obter mais informações sobre como usar parâmetros de matriz de estilo C, também conhecidos como matrizes compatíveis, com C++/WinRT, consulte parâmetros de matriz.

Métodos estáticos e de instância

Um método declarado com um static modificador prefixado é um método estático. Um método estático não tem acesso a uma instância específica e, portanto, só pode acessar diretamente outros membros estáticos da classe.

Um método declarado sem um static modificador é um método de instância. Um método de instância tem acesso a uma instância específica e pode acessar membros estáticos e de instância da classe.

A classe Entity a seguir tem membros estáticos e de instância.

runtimeclass Entity
{
    Int32 SerialNo { get; };
    static Int32 GetNextSerialNo();
    static void SetNextSerialNo(Int32 value);
}

Cada instância de Entidade contém seu próprio número de série (e, presumivelmente, algumas outras informações que não são mostradas aqui). Internamente, o construtor Entity (que é como um método de instância) inicializa a nova instância com o próximo número de série disponível.

A propriedade SerialNo fornece acesso ao número de série para a instância na qual você invoca o método de obtenção de propriedade .

Os métodos estáticos GetNextSerialNo e SetNextSerialNo podem acessar o próximo membro estático de número de série disponível interno da classe Entity .

Métodos substituíveis e protegidos

Todos os métodos em um tipo de Windows Runtime são efetivamente virtuais. Quando um método virtual é invocado, o tipo de tempo de execução da instância para o qual essa invocação ocorre determina a implementação real do método para invocar.

Um método pode ser substituído em uma classe derivada. Quando uma declaração de método de instância inclui um overridable modificador, o método pode ser substituído por classes derivadas. Se uma classe derivada realmente substitui um método de classe base substituível é determinado pela implementação; não está presente nos metadados. Se uma classe derivada redeclara um método na classe base, ele declara um novo método que se senta ao lado do método de classe derivada, em vez de substituí-lo.

Quando uma declaração de método de instância inclui um protected modificador, o método fica visível apenas para classes derivadas.

Eventos

Uma declaração de evento é um membro que especifica que uma classe é uma fonte de evento. Essa fonte de evento fornece notificações para qualquer destinatário que implemente um delegado (um método com uma assinatura específica).

Você declara um evento usando a event palavra-chave, seguido pelo nome do tipo delegado (que descreve a assinatura de método necessária), seguido pelo nome do evento. Aqui está um evento de exemplo que usa um tipo de delegado existente da plataforma.

runtimeclass Area
{
    ...
    event Windows.UI.Xaml.WindowSizeChangedEventHandler SizeChanged;
    ...
}

Uma declaração de evento adiciona implicitamente dois métodos à classe: um método add , que um cliente chama para adicionar um manipulador de eventos à origem e um método remove , que um cliente chama para remover um manipulador de eventos adicionado anteriormente. Aqui estão mais exemplos.

// Instance event with no meaningful payload.
event Windows.Foundation.TypedEventHandler<BasicClass, Object> Changed;

// Instance event with event parameters.
event Windows.Foundation.TypedEventHandler<BasicClass, BasicClassSaveCompletedEventArgs> SaveCompleted;

// Static event with no meaningful payload.
static event Windows.Foundation.EventHandler<Object> ResetOccurred;

// Static event with event parameters.
static event Windows.Foundation.EventHandler<BasicClassDeviceAddedEventArgs> DeviceAdded;

Por convenção, dois parâmetros são sempre passados para um manipulador de eventos Windows Runtime: a identidade do remetente e um objeto de argumentos de evento. O remetente é o objeto que levantou o evento ou nulo para eventos estáticos. Se o evento não tiver nenhum conteúdo significativo, os argumentos de evento serão um Objeto cujo valor é nulo.

Delegados

Um tipo delegado especifica um método com uma lista de parâmetros específica e um tipo de retorno. Uma única instância de um evento pode conter qualquer número de referências a instâncias de seu tipo delegado. A declaração é semelhante à de um método de membro regular, exceto que ela existe fora de uma classe de runtime e é prefixada com a delegate palavra-chave.

Um delegado possibilita tratar métodos como entidades que podem ser atribuídas a variáveis e passadas como parâmetros. Os delegados são semelhantes ao conceito de ponteiros de função encontrados em algumas outras linguagens. Mas, ao contrário dos ponteiros de função, os delegados são orientados a objetos e são type-safe.

Se não quisermos usar o tipo de delegado WindowSizeChangedEventHandler da plataforma, podemos definir nosso próprio tipo de delegado.

delegate void SizeChangedHandler(Object sender, Windows.UI.Core.WindowSizeChangedEventArgs args);

Uma instância do nosso tipo delegado SizeChangedHandler pode referenciar qualquer método que usa dois argumentos (um objeto e um WindowSizeChangedEventArgs) e retorna nulo. Depois de discutirmos structs, você também poderá substituir o parâmetro WindowSizeChangedEventArgs por um tipo dergs de evento próprio.

Uma propriedade interessante e útil de um delegado é que ele não sabe ou se preocupa com a classe do método que ele faz referência; tudo o que importa é que o método referenciado tem os mesmos parâmetros e o tipo de retorno que o delegado.

Opcionalmente, você pode atribuir uma declaração delegada com [uuid(...)].

Veja também representantes retornando HRESULT.

Estruturas

Um struct é uma estrutura de dados que pode conter membros de dados (campos). Mas, ao contrário de uma classe, um struct é um tipo de valor.

Os structs são particularmente úteis para estruturas de dados pequenas que têm semântica de valor. Números complexos ou pontos em um sistema de coordenadas são bons exemplos de structs. O uso de structs em vez de classes para estruturas de dados pequenas pode fazer uma grande diferença no número de alocações de memória executadas por um aplicativo.

Vamos usar um exemplo para contrastar classes e structs. Aqui está uma versão do Ponto primeiro como uma classe.

runtimeclass Point
{
    Point(Int32 x, Int32 y);
    Int32 x;
    Int32 y;
}

Este programa C# cria e inicializa uma matriz de 100 instâncias de Point. Com o Ponto implementado como uma classe, 101 objetos separados são instanciados: um para o próprio objeto de matriz; e um para cada um dos elementos de 100 Pontos .

class Test
{
    static Test()
    {
        Point[] points = new Point[100];
        for (Int32 i = 0; i < 100; ++i) points[i] = new Point(i, i);
    }
}

Uma alternativa mais performante é tornar Point um struct, em vez de uma classe.

struct Point
{
    Int32 x;
    Int32 y;
};

Agora, apenas um objeto é instanciado — o próprio objeto de matriz. Os elementos Point são armazenados em linha dentro da matriz; uma disposição de memória que os caches de processador são capazes de usar para efeito avançado.

Alterar um struct é uma alteração de quebra binária. Portanto, os structs implementados como parte do próprio Windows não são alterados uma vez introduzidos.

Interfaces

Uma interface define um contrato que pode ser implementado por classes. Uma interface pode conter métodos, propriedades e eventos, assim como classes.

Ao contrário de uma classe, uma interface não fornece implementações dos membros que ela define. Ele apenas especifica os membros que devem ser fornecidos por qualquer classe que implemente a interface.

As interfaces podem exigir uma classe que implemente a interface para também implementar outras interfaces. No exemplo a seguir, a interface IComboBox exige que qualquer classe que implemente iComboBox também implementeiTextBox e IListBox. Além disso, uma classe que implementa o IComboBox também deve implementar o IControl. Isso ocorre porque o ITextBox e o IListBox exigem isso.

interface IControl
{
    void Paint();
}

interface ITextBox requires IControl
{
    void SetText(String text);
}

interface IListBox requires IControl
{
    void SetItems(String[] items);
}

interface IComboBox requires ITextBox, IListBox
{
    ...
}

Uma classe pode implementar zero ou mais interfaces. No próximo exemplo, a classe EditBox implementa IControl e IDataBound.

interface IDataBound
{
    void Bind(Binder b);
}

runtimeclass EditBox : IControl, IDataBound
{
}

Para tipos Windows Runtime na plataforma Windows, uma interface será definida se os desenvolvedores que consomem esses tipos devem implementar a interface. Outro caso de uso para definir uma interface é quando várias classes de runtime implementam a interface, e os desenvolvedores que consomem essas classes de runtime acessarão diferentes tipos de objeto genericamente (e, portanto, polimórfico) por meio dessa interface comum.

Observação

Pense duas vezes antes de usar a requires palavra-chave em MIDL 3.0. Isso pode levar a designs confusos, especialmente quando o controle de versão é levado em conta.

Enumerações

Um tipo de enumeração (ou tipo enumerado ou enumeração) é um tipo de valor distinto com um conjunto de constantes nomeadas. O exemplo a seguir define e usa um tipo de enumeração chamado Cor com três valores constantes: Vermelho, Verde e Azul.

enum Color
{
    Red,
    Green,
    Blue, // Trailing comma is optional, but recommended to make future changes easier.
};

Cada tipo de enumeração tem um tipo integral correspondente chamado o tipo subjacente do tipo de enumeração. O tipo subjacente de uma enumeração é Int32 ou UInt32.

O Windows Runtime dá suporte a dois tipos de enumerações: enumerações normais e enumerações de sinalizadores. Uma enumeração do tipo normal expressa um conjunto de valores exclusivos; enquanto um dos sinalizadores representa um conjunto de valores boolianos. Para habilitar operadores bit a bit para uma enumeração de sinalizadores, o compilador MIDL 3.0 gera sobrecargas do operador C++.

Uma enumeração de sinalizadores tem o [flags] atributo aplicado. Nesse caso, o tipo subjacente da enumeração é UInt32. Quando o [flags] atributo não está presente (uma enumeração normal), o tipo subjacente da enumeração é Int32. Não é possível declarar uma enumeração como qualquer outro tipo.

[flags]
enum SetOfBooleanValues
{
    None   = 0x00000000,
    Value1 = 0x00000001,
    Value2 = 0x00000002,
    Value3 = 0x00000004,
};

O formato de armazenamento de um tipo de enumeração e o intervalo de valores possíveis são determinados por seu tipo subjacente. O conjunto de valores que um tipo de enumeração pode assumir não é limitado por seus membros enumerados declarados.

O exemplo a seguir define um tipo de enumeração chamado Alignment, com um tipo subjacente de Int32.

enum Alignment
{
    Left = -1,
    Center = 0,
    Right = 1
};

Como também é verdadeiro para C e C++, uma enumeração MIDL 3.0 pode incluir uma expressão constante que especifica o valor do membro (conforme visto acima). O valor constante de cada membro de enumeração deve estar no intervalo do tipo subjacente da enumeração. Quando uma declaração de membro de enumeração não especifica explicitamente um valor, o membro recebe o valor zero (se for o primeiro membro no tipo de enumeração) ou o valor do membro de enumeração anterior textually mais um.

O exemplo a seguir define um tipo de enumeração chamado Permissões, com um tipo subjacente de UInt32.

[flags]
enum Permissions
{
    None = 0x0000,
    Camera = 0x0001,
    Microphone = 0x0002
};

Atributos

Tipos, membros e outras entidades no código-fonte MIDL 3.0 dão suporte a modificadores que controlam determinados aspectos de seu comportamento. Por exemplo, a acessibilidade de um método é controlada usando o protected modificador de acesso. O MIDL 3.0 generaliza essa funcionalidade de modo que os tipos de informações declarativas definidos pelo usuário possam ser anexados a entidades do programa e recuperados em tempo de execução dos metadados.

Os programas especificam essas informações declarativas adicionais, definindo e usando os atributos.

O próximo exemplo define um atributo HelpAttribute , que pode ser colocado em entidades de programa para fornecer links para a documentação associada. Como você pode ver, um atributo é essencialmente um tipo de struct, portanto, ele não tem um construtor e contém apenas membros de dados.

[attributeusage(target_runtimeclass, target_event, target_method, target_property)]
attribute HelpAttribute
{
    String ClassUri;
    String MemberTopic;
}

Um atributo pode ser aplicado dando seu nome, juntamente com quaisquer argumentos, dentro de colchetes pouco antes da declaração associada. Se o nome de um atributo terminar no Atributo, essa parte do nome poderá ser omitida quando o atributo for referenciado. Por exemplo, o atributo HelpAttribute pode ser usado assim.

[Help("https://docs.contoso.com/.../BookSku", "BookSku class")]
runtimeclass BookSku : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
    [Help("https://docs.contoso.com/.../BookSku_Title", "Title method")]
    String Title;
}

Você pode aplicar o mesmo atributo a várias declarações usando um bloco de escopo seguindo o atributo. Ou seja, um atributo imediatamente seguido por chaves em torno das declarações às quais o atributo se aplica.

runtimeclass Widget
{
    [Help("https://docs.contoso.com/.../Widget", "Widget members")]
    {
        void Display(String text);
        void Print();
        Single Rate;
    }
}

Os atributos implementados como parte do próprio Windows geralmente estão no namespace do Windows.Foundation .

Conforme mostrado no primeiro exemplo, você usa o [attributeusage(<target>)] atributo na definição de atributo. Os valores de destino válidos sãotarget_all, , , target_enum, target_event, target_field, target_interface, target_method, target_parameter, target_property, e target_structtarget_runtimeclass. target_delegate Você pode incluir vários destinos dentro dos parênteses, separados por vírgulas.

Outros atributos que você pode aplicar a um atributo são [allowmultiple] e [attributename("<name>")].

Tipos parametrizados

O exemplo a seguir produz o erro MIDL2025: [msg]syntax error [context]: expecting > or, near ">>".

Windows.Foundation.IAsyncOperation<Windows.Foundation.Collections.IVector<String>> RetrieveCollectionAsync();

Em vez disso, insira um espaço entre os dois > caracteres para que o par de caracteres de fechamento de modelo não seja mal interpretado como um operador de turno direito.

Windows.Foundation.IAsyncOperation<Windows.Foundation.Collections.IVector<String> > RetrieveCollectionAsync();

O exemplo a seguir produz o erro MIDL2025: [msg]syntax error [context]: expecting > or, near "[". Isso ocorre porque é inválido usar uma matriz como um argumento de tipo de parâmetro para uma interface parametrizada.

Windows.Foundation.IAsyncOperation<Int32[]> RetrieveArrayAsync();

Para a solução, consulte Retornando uma matriz de forma assíncrona.