Compartilhar via


O sistema de tipos do WinRT (Windows Runtime)

Observações gerais

Todos os tipos, exceto os tipos fundamentais, devem estar contidos em um namespace. Não é válido para um tipo estar no namespace global.

Os tipos fornecidos por Windows estão todos contidos no namespace Windows.*. Os tipos winRT que não são fornecidos por Windows (incluindo tipos WinRT fornecidos por outras partes da Microsoft) devem residir em um namespace diferente de Windows.*.

Com exceção das interfaces, todos os tipos de WinRT devem ter visibilidade pública. As interfaces WinRT podem, opcionalmente, ter visibilidade privada. Todos os outros tipos winrt definidos pelo usuário (structs, classes, enums, delegados, atributos) devem ter visibilidade pública.

O WinRT não dá suporte a tipos aninhados. Nenhum tipo WinRT pode incluir outro tipo; nenhum tipo WinRT pode ser aninhado dentro de outro tipo.

Nem todos os aspectos do sistema de tipos WinRT estão disponíveis para você, como um desenvolvedor de terceiros.

  • O WinRT dá suporte à parametrização de interfaces e delegados. No entanto, nesta versão, o WinRT não dá suporte à definição de tipos parametrizados por terceiros. Há suporte apenas para os tipos parametrizados incluídos no sistema no namespace Windows.* .

  • O WinRT dá suporte à composição de classe como um mecanismo de herança de runtime. No entanto, nesta versão, o WinRT não dá suporte à definição de uma classe composável raiz. Há suporte apenas para as classes composáveis raiz incluídas no sistema no namespace Windows.*.

Namespaces winRT e nomes de tipo são de preservação de maiúsculas e minúsculas, mas não diferenciam maiúsculas de minúsculas, semelhantes ao Sistema de Arquivos Windows e ao Registro de Windows. Isso significa que você não pode ter namespaces ou nomes de tipo que variam apenas por caso. Por exemplo, você não pode ter Foo.SomeType e foo. AnotherType, nem você pode ter Foo.SomeType e Foo.someType.

Um identificador WinRT deve estar em conformidade com a gramática a seguir. Observe que há suporte apenas para caracteres definidos no Unicode 3.0 e anterior.

    identifier-or-keyword: 
        identifier-start-character   identifier-continuation-characters* 
    identifier-start-character: 
        letter-character
        underscore (U+005F) 
    identifier-continuation-characters: 
        identifier-continuation-character
        identifier-continuation-characters   identifier-continuation-character 
    identifier-continuation-character: 
        identifier-start-character 
        decimal-digit-character
        connecting-character
        combining-character
        formatting-character 
    letter-character: 
        A Unicode 3.0 character of classes Lu, Ll, Lt, Lm, Lo, or Nl 
    combining-character: 
        A Unicode 3.0 character of classes Mn or Mc 
    decimal-digit-character: 
        A Unicode 3.0 character of the class Nd 
    connecting-character: 
        A Unicode 3.0 character of the class Pc 
    formatting-character: 
        Zero Width Non-Joiner (U+200C)
        Zero Width Joiner (U+200D)

Tipos parametrizados

O WinRT dá suporte à parametrização de tipos de interfaces e delegados. Os tipos parametrizados permitem que uma família de interfaces seja definida que pode ser processada polimorficamente em linguagens de programação que dão suporte ao polimorfismo paramétrico.

Uma instanciação de tipo parametrizado ocorre quando um tipo parametrizado é especificado com uma lista de argumentos de tipos em um contexto de tipo, como uma posição de parâmetro de método. Por exemplo, HRESULT foo(X<Y> x) cria uma instância do tipo parametrizado nomeado por X com o tipo Y como seu primeiro e único argumento de tipo.

Uma interface ou delegado WinRT nãoparameterizado recebe um GUID para identificar exclusivamente a interface subjacente como distinta de outras interfaces no mesmo objeto. Uma interface parametrizada (por exemplo) IVector<T>ou delegado (por exemplo, EventHandler<T>) é atribuída a uma PIID (ID de interface parametrizada), que é um GUID que identifica exclusivamente esse tipo parametrizado. Isso não é uma IID. Esse GUID é usado na geração de IIDs para instâncias de tipo parametrizado (por exemplo, IVector<int>).

Lista de argumentos de tipo parametrizado

Esses tipos WinRT têm permissão para aparecer na lista de argumentos para um tipo parametrizado.

  • Tipos fundamentais do WinRT (por exemplo, Boolean, Int32, String, Guid etc.)
  • Enumerações do WinRT
  • Structs do WinRT
  • Interfaces do WinRT
  • Representantes do WinRT
  • Classes de runtime do WinRT
  • Outras instanciações de tipo parametrizado (por exemplo, IVector<IVector<int>>) Qualquer outro tipo é proibido de aparecer em uma lista de argumentos de tipo parametrizado. Por exemplo, matrizes.

Instâncias de tipo parametrizado

Uma instância de tipo parametrizado pode aparecer em qualquer contexto que um tipo não parametrizado possa aparecer. Uma instância de interface parametrizada pode ser usada em qualquer lugar em que uma interface possa ser usada, como na lista de interfaces implementadas por uma classe de runtime. Uma instância delegada parametrizada pode ser usada em qualquer lugar em que um delegado possa ser usado, como na definição de um evento.

Um parâmetro de tipo parametrizado pode aparecer: na interface parametrizada ou na definição de delegado; ou, qualquer lugar em que um tipo normal possa aparecer normalmente. Se um parâmetro for exibido em que apenas uma interface pode aparecer, esse parâmetro será restrito a ser uma interface. Se um parâmetro for exibido em um contexto em que qualquer tipo possa aparecer, esse parâmetro não será restrito; e assim por diante. Os argumentos para instâncias de tipo parametrizado devem atender a todas essas restrições.

Geração de guid para tipos parametrizados

O algoritmo de geração de guid deve atender a esses requisitos.

  • Quando um tipo parametrizado é instanciado duas vezes com os mesmos argumentos, ambas as instanciações devem receber a mesma interface ou corpo delegado e a mesma IID.
  • Se dois tipos parametrizados diferentes forem instanciados, mesmo com os mesmos argumentos, é estatisticamente improvável que eles sejam atribuídos à mesma IID.
  • Se um tipo parametrizado for instanciado duas vezes, mas com argumentos diferentes, deverá ser estatisticamente improvável que as duas instanciações sejam atribuídas à mesma IID.

Observação

Não é permitido que um tipo parametrizado seja instanciado com a mesma instanciação de um argumento ou como um argumento para qualquer outro tipo parametrizado que aparece na lista de argumentos (ou seja, instanciação circular). Por exemplo, se X = Foo<Y>a Y = Foo<X>não circularidade tiver sido violada. No entanto, se X = Foo<Y> e Y = Foo<int>, em seguida, a não circularidade foi mantida.

O algoritmo de instanciação é o seguinte.

  1. Cada tipo parametrizado recebe uma ID de interface parametrizada por seu autor— PIID abreviada. Observe que um PIID não é um IIDs, nem é passado como um argumento para QueryInterface. O autor deve garantir que o PIID seja exclusivo para o tipo parametrizado.
  2. A assinatura de tipo para um tipo base é uma cadeia de caracteres de octeto ASCII (consulte a tabela abaixo). Por exemplo, Int32 é "i4".
  3. A assinatura de tipo para uma interface que não é uma instância de interface parametrizada é sua IID codificada em ASCII em forma tracejada e delimitada por chaves. Por exemplo, "{00000000-0000-0000-0000-000000000000}".
  4. A assinatura de tipo para um delegado que não é uma instância delegada parametrizada é a cadeia de caracteres "delegate" e, em seguida, a IID como com interfaces. A gramática detalhada aparece em seguida.
  5. O guid para um tipo parametrizado é calculado de acordo com essa gramática.
    • de acordo com uuid rfc 4122, compute o ver 5 sha-1 gerado hash de signature_octets — isso usa um único guid winrt pinterface/pintergroup como o namespace conforme descrito no rfc 4122/4.3, e a assinatura do pinterface/pintergroup e os args que é instanciado como a cadeia de caracteres de nome.
    • a instanciação de pinterface é atribuída a este guid e a assinatura de 4.
    signature_octets => guid_to_octets(wrt_pinterface_namespace) string_to_utf8_octets(ptype_instance_signature)

        wrt_pinterface_namespace => "11f47ad5-7b73-42c0-abae-878b1e16adee"

        ptype_instance_signature => pinterface_instance_signature | pdelegate_instance_ signature

        pinterface_instance _signature => "pinterface(" piid_guid  ";" args ")"

        pdelegate_instance _signature => "pinterface(" piid_guid ";" args ")"

        piid_guid => guid

        args => arg | arg ";" args

        arg => type_signature

        type_signature => base_type_identifer | com_interface_signature | interface _signature | delegate_signature  | interface_group_signature | runtime_class_signature | struct_signature | enum_signature | pinterface_instance_signature | pdelegate_instance_signature
        com_interface_signature => "cinterface(IInspectable)"

        base_type_identifier is defined below

        interface_signature => guid

        interface_group_signature => "ig(" interface_group_name ";" default_interface ")"

        runtime_class_signature => "rc(" runtime_class_name ";" default_interface ")"

        default_interface => type_signature

        struct_signature => "struct(" struct_name ";" args ")"

        enum_signature => "enum(" enum_name ";" enum_underlying_type ")"

        enum_underlying_type => type_signature

        delegate_signature => "delegate(" guid ")"

        guid => "{" dashed_hex "}"

        dashed_hex is the format that uuidgen writes in when passed no arguments.

            dashed_hex => hex{8} "-" hex{4} "-" hex{4} "-" hex{4} "-" hex{12}

            hex => [0-9a-f]
  1. Quando uma instanciação de tipo p é passada como um argumento para anther pinterface, a assinatura computada pela etapa 3 é usada como a assinatura de tipo no elemento gramatical 'pinterface_instance_signature' ou 'pdelegate_instance_signature', conforme apropriado.

Esses nomes são usados para tipos base quando são exibidos.

  • UInt8 mapeia para "u1"
  • Int32 mapeia para "i4"
  • UInt32 mapeia para "u4"
  • Int64 mapeia para "i8"
  • UInt64 mapeia para "u8"
  • Mapas únicos para "f4"
  • Mapas duplos para "f8"
  • Boolean mapeia para "b1"
    • Observe que, para obter suporte ao parâmetro de tipo booliano , você precisa adicionar /Yc:wchar_t para habilitar wchar_t como um tipo interno.
  • Char16 mapeia para "c2"
  • A cadeia de caracteres é mapeada para "cadeia de caracteres"
  • Guid mapeia para "g16"

Os nomes acima diferenciam maiúsculas de minúsculas. Com exceção de String, o nome do tipo usa um único caractere para sugerir o tipo de dados, seguido pelo seu tamanho em bytes. Esses nomes foram escolhidos: para serem concisos (para evitar tamanhos grandes em assinaturas de tipo struct); para não parecer confusamente semelhante ao nome WinRT, ao nome RIDL nem ao nome de projeção de linguagem para qualquer tipo; e ainda permanecer aproximadamente legível por humanos.

Para enum_type_signature, o único valor "underlying_type" válido é o de Int32 ou UInt32.

Para struct_type_signature, args é uma lista em ordem de type_signatures para os campos do struct. Podem ser tipos base ou outros tipos de struct.

Struct_name e enum_name são qualificados para namespace, usando o período "" como delimitadores. Por exemplo, "namespace X { struct A{ int; }; }; }" torna-se "struct(X.A;i4)".

Default_interface deve ser a interface, a instância de interface p, o delegado ou a instância p-delegate especificada como padrão na classe de runtime ou no corpo do grupo de interface, usando o atributo IDL '[default]'.

Observe que os atributos personalizados são ignorados; presumidamente não ter nenhum efeito no marshaling.

Controle de versão

Todos os tipos WinRT, exceto tipos fundamentais, devem ter um atributo Version. As projeções de linguagem usam as informações do atributo Version para habilitar a compatibilidade com versões anteriores e para cenários de iluminação. Tipos de terceiros devem incluir um atributo Version, mas devem ser ignorados por projeções de linguagem. Os componentes WinRT de terceiros são empacotados exclusivamente no aplicativo, portanto, eles nunca podem alterar a versão independentemente do próprio aplicativo.

Opcionalmente, o atributo Version pode ser aplicado aos membros da interface (métodos, propriedades e eventos). Isso destina-se à criação de classe de alto nível em C#/VB e C++/CX. Os atributos de versão em membros da interface, mesmo Windows membros da interface do sistema, devem ser ignorados em runtime por projeções de linguagem.

O atributo Version inclui um parâmetro de construtor inteiro sem sinal de 32 bits. Para tipos winrt Windows, esse valor é o valor NTDDI para a versão de Windows o constructo de tipo associado foi definido pela primeira vez. Para tipos de terceiros, o significado desse valor é até o autor do tipo.

Windows structs, delegados e interfaces do sistema são imutáveis uma vez definidos. Eles podem nunca ser modificados em nenhuma versão de Windows subsequente.

Windows enumerações do sistema e classes de runtime são aditivamente versões. As enumerações podem adicionar novos valores de enumeração em versões Windows subsequentes. As classes podem adicionar novas interfaces implementadas (incluindo interfaces estáticas, de fábrica de ativação, de fábrica de composição, substituíveis e protegidas) nas versões Windows subsequentes. Mais detalhes sobre o controle de versão aditivo estão incluídos nas seções para enumerações e classes de runtime.

Namespaces

Um namespace é um escopo de nomenclatura usado para organizar o código e evitar colisões de nomenclatura. Todos os tipos nomeados no sistema de tipos WinRT (enumerações, structs, delegados, interfaces e classes de runtime) vivem em um namespace. Um namespace pode conter outros namespaces.

Tipos fundamentais

O sistema de tipo WinRT inclui um conjunto principal de tipos primitivos internos.

Tipo WinRT Descrição do tipo
Int16 um inteiro com sinal de 16 bits
Int32 um inteiro com sinal de 32 bits
Int64 um inteiro com sinal de 64 bits
UInt8 um inteiro sem sinal de 8 bits
UInt16 um inteiro sem sinal de 16 bits
UInt32 um inteiro sem sinal de 32 bits
UInt64 um inteiro sem sinal de 64 bits
Single um número de ponto flutuante IEEE 754 de 32 bits
Double um número de ponto flutuante IEEE 754 de 64 bits
Char16 um valor não numérico de 16 bits que representa uma unidade de código UTF-16
Booliano um valor booliano de 8 bits
String uma sequência imutável de Char16 usada para representar texto
Guid Um Guid padrão de 128 bits

Enumerações

Um tipo enum é um tipo de valor diferente com um conjunto de constantes nomeadas.

Cada tipo de enumeração tem um tipo integral correspondente chamado o tipo subjacente do tipo de enumeração. Os únicos tipos subjacentes de enumeração legal no WinRT são Int32 e UInt32.

Uma enumeração com um tipo subjacente de UInt32 deve carregar o FlagsAttribute. Uma enumeração com um tipo subjacente de Int32 não deve carregar o FlagsAttribute.

Uma enumeração deve ter visibilidade pública.

Enumeração de versão

Uma enumeração é additively versionable. As versões subsequentes de uma determinada enumeração podem adicionar valores (também conhecidos como constantes nomeadas). Valores pré-existentes não podem ser removidos ou alterados. Os valores de enumeração opcionalmente carregam o VersionAttribute para distinguir quando valores específicos foram adicionados ao tipo de enumeração. Os valores de enumeração sem um VersionAttribute são considerados com o mesmo valor de versão que o tipo de enumeração que inclui.

Estruturas

Um struct é um tipo de registro com um ou mais campos. Os structs são sempre passados e retornados por valor. Os campos Struct podem ser apenas primitivos, enumerações, structs, cadeias de caracteres e IReference<T> (os dois últimos são os únicos dois tipos de campo alocados por heap).

Os structs devem ter visibilidade pública.

Em geral, um struct deve ter pelo menos um campo (há raras exceções, como tipos que representam contratos e atributos de metadados). Todos os campos de um struct devem ser públicos.

Um struct não pode ser genérico nem parametrizado.

Interfaces

Uma interface é um contrato que consiste em um grupo de membros de tipo relacionados cujo uso é definido, mas cuja implementação não é. Uma definição de interface especifica os membros da interface— métodos, propriedades e eventos. Não há nenhuma implementação associada a uma interface.

As interfaces não parametrizadas devem ter uma ID de interface exclusiva (também conhecida como IID) especificada por meio de um GuidAttribute. Uma interface parametrizada deve ter uma ID de interface parametrizada exclusiva (também conhecida como PIID) especificada por meio de um GuidAttribute. O PIID é usado para gerar uma IID para uma instância de interface parametrizada específica por meio do algoritmo especificado acima.

Uma interface pode ter visibilidade pública ou privada. Isso reflete o fato de que algumas interfaces representam contratos compartilhados implementados por várias classes WinRT, enquanto outras interfaces representam membros implementados por uma única classe WinRT. Uma interface de visibilidade privada deve especificar a classe WinRT à qual é exclusiva por meio do ExclusiveToAttribute. As interfaces privadas só podem ser implementadas pela classe WinRT especificada no ExclusiveToAttribute.

IInspectable e IUnknown

Quando se trata de interfaces, o WinRT não tem noção de herança. Em vez disso, há a ideia de que uma interface pode exigir outra interface. Para obter mais informações, especificamente sobre a palavra-chave MIDL 3.0 requires , consulte interfaces MIDL 3.0.

Todas as interfaces WinRT exigem implicitamente IInspectable; e, por sua vez, IInspectable requer IUnknown. O IUnknown define três métodos: QueryInterface, AddRef e Release de acordo com o uso com tradicional. O IInspectable define três métodos além dos métodos IUnknown: GetIids, GetRuntimeClassName e GetTrustLevel. Esses três métodos permitem que o cliente do objeto recupere informações sobre o objeto. Em particular, IInspectable.GetRuntimeClassName permite que o cliente de um objeto recupere um nome de tipo WinRT que pode ser resolvido em metadados para habilitar a projeção de idioma.

A interface requer

Conforme mencionado acima, uma interface pode especificar que requer uma ou mais outras interfaces que devem ser implementadas em qualquer objeto que implemente a interface em questão. Por exemplo, se IButton exigir IControl, qualquer classe que implemente IButton também precisará implementar o IControl.

Mas nem o sistema de tipos WinRT nem a ABI têm um conceito de herança de interface. Portanto, a ideia de adicionar novas funcionalidades implementando novas interfaces que herdam de interfaces existentes (por exemplo, IFoo2 herda do IFoo) não tem significado. É verdade que uma projeção de linguagem WinRT pode usar uma relação de herança para facilitar o consumo nesse idioma específico, e uma classe de runtime pode usar herança, mas a herança de interface não existe no contexto da criação midl 3.0 (consulte interfaces MIDL 3.0).

Interfaces parametrizadas

As interfaces dão suporte à parametrização de tipo. Uma definição de interface parametrizada especifica uma lista de parâmetros de tipo, além da lista de membros da interface e interfaces necessárias. Uma interface necessária de uma interface parametrizada pode compartilhar a mesma lista de argumentos de tipo, de modo que um argumento de tipo único seja usado para especificar a instância parametrizada da interface e da interface necessária (por exemplo, IVector<T> requires IIterable<T>). A assinatura de qualquer membro (ou seja, método, propriedade ou evento) de uma interface parametrizada pode fazer referência a um tipo da lista de argumentos de tipo da interface parametrizada (por exemplo, IVector<T>.SetAt([in] UInt32 index, [in] T value)).

Terceiros não podem definir novas interfaces parametrizadas. Há suporte apenas para as interfaces parametrizadas definidas pelo sistema.

Delegados

Um delegado é um tipo WinRT que atua como um ponteiro de função de tipo seguro. Um delegado é essencialmente um objeto WinRT simples que expõe uma única interface que herda do IUnknown e define um único método chamado Invoke. Invocar o delegado, por sua vez, invoca o método que ele faz referência. Os delegados são frequentemente (mas não exclusivamente) usados para definir eventos WinRT.

Um delegado WinRT é um tipo nomeado e define uma assinatura de método. As assinaturas de método delegado seguem as mesmas regras para parâmetros como os métodos de interface. Os nomes de assinatura e parâmetro do método Invoke devem corresponder à definição do delegado.

Assim como as interfaces, os delegados não parametrizados devem ter uma ID de interface exclusiva (também conhecida como IID) especificada por meio de um GuidAttribute. A IID do delegado é usada como IID da interface de método único usada para implementar o delegado. Os delegados parametrizados devem ter uma ID de interface parametrizada exclusiva (também conhecida como PIID) especificada por meio de um GuidAttribute. O PIID é usado para gerar uma IID para uma instância delegada parametrizada específica por meio do algoritmo especificado acima.

Um delegado deve ter visibilidade pública.

IUnknown

Observe que, ao contrário das interfaces do WinRT, os delegados implementam o IUnknown, mas não iInspectable. Isso significa que eles não podem ser inspecionados para obter informações de tipo no runtime.

Delegados parametrizados

Representantes dão suporte à parametrização de tipo. Uma definição de delegado parametrizada especifica uma lista de parâmetros de tipo, além da assinatura de método tradicional, conforme especificado acima. Na assinatura do método, qualquer parâmetro pode ser especificado como um dos tipos da lista de argumentos de tipo de delegados parametrizados.

Terceiros não podem definir novos delegados parametrizados. Há suporte apenas para os delegados parametrizados definidos pelo sistema.

Membros da interface

As interfaces WinRT dão suporte a três tipos de membros: métodos, propriedades e eventos. As interfaces podem não ter campos de dados.

Métodos

As interfaces WinRT dão suporte a métodos que levam zero ou mais parâmetros e que retornam um HRESULT que indica o êxito ou a falha da chamada de método. Opcionalmente, um método pode indicar um parâmetro individual a ser projetado como o valor retornado em idiomas baseados em exceção. Esse parâmetro de valor retornado, se especificado, deve ser o último parâmetro na assinatura do método.

Um método deve ter visibilidade pública.

Um método pode não usar números variáveis de argumentos. Um método pode não ter parâmetros opcionais nem parâmetros com valores padrão.

Um método pode não ser parametrizado. Delegados parametrizados e métodos de interfaces parametrizadas podem usar parâmetros de tipo do tipo que contém na assinatura do método.

Parâmetros

Todos os parâmetros de método, exceto os parâmetros de comprimento da matriz (descritos abaixo) devem ter um nome e um tipo. Observe que os valores retornados devem especificar um nome como os parâmetros fazem. Os nomes de parâmetro de método, incluindo o nome do tipo de retorno, devem ser exclusivos dentro do escopo do método.

Somente parâmetros para delegados parametrizados e membros de interfaces parametrizadas podem especificar um tipo parametrizado para o tipo de parâmetro. Os métodos podem não ser individualmente parametrizados. Os parâmetros sempre podem especificar instâncias de tipo parametrizado (por exemplo, IVector<int>) como o tipo de parâmetro.

Todos os parâmetros de método devem estar exclusivamente dentro ou fora dos parâmetros. Não há suporte para parâmetros de entrada/saída.

Embora um método em uma interface WinRT deva retornar um HRESULT, um método pode indicar opcionalmente que seu parâmetro final de saída deve ser usado como o valor retornado quando o método é projetado em idiomas baseados em exceção. Esses parâmetros são conhecidos como parâmetros [out, retval] após a sintaxe MIDL usada para declará-los. Quando um parâmetro [out, retval] é especificado, ele deve ser o último parâmetro na assinatura do método.

Além de [out, retval] precisar aparecer no final da lista de parâmetros, não há outros requisitos de ordenação para parâmetros de saída.

Parâmetros de matriz

Os métodos WinRT dão suporte a parâmetros de matriz de conformidade. Matrizes nunca podem ser usadas, exceto como parâmetros. Eles não podem ser tipos nomeados autônomos e não podem ser usados como um tipo de campo struct. Parâmetros de matriz podem ser usados como inparâmetros oute retval parâmetros.

O WinRT dá suporte a parâmetros de matriz da maioria dos tipos WinRT, incluindo tipos fundamentais (incluindo cadeia de caracteres e guid), structs, enumerações, delegados, interfaces e classes de runtime. Matrizes de outras matrizes não são permitidas.

Como eles são compatíveis, os parâmetros de matriz sempre devem ser imediatamente precedidos na lista de parâmetros por um parâmetro para o tamanho da matriz. O parâmetro de tamanho da matriz deve ser um UInt32. O parâmetro de tamanho da matriz não tem um nome.

O WinRT dá suporte a três estilos diferentes de passagem de matriz.

  • PassArray. Esse estilo é usado quando o chamador fornece uma matriz para o método. Nesse estilo, o parâmetro de tamanho da matriz e o parâmetro de matriz são ambos in parâmetros.
  • Fillarray. Esse estilo é usado quando o chamador fornece uma matriz para o método preencher, até um tamanho máximo de matriz. Nesse estilo, o parâmetro de tamanho da matriz é um in parâmetro, enquanto o parâmetro de matriz é um out parâmetro. Ao usar o estilo FillArray, o parâmetro de matriz pode, opcionalmente, especificar um dos outros parâmetros como o parâmetro de comprimento da matriz. Veja os detalhes abaixo.
  • ReceiveArray. Esse estilo é usado quando o chamador recebe uma matriz alocada pelo método. Nesse estilo, o parâmetro de tamanho da matriz e o parâmetro de matriz são ambos out parâmetros. Além disso, o parâmetro de matriz é passado por referência (ou seja, ArrayType**, em vez de ArrayType*).

Observação

A combinação de um out parâmetro de tamanho de matriz, mas um parâmetro de in matriz, não é válida no WinRT.

Quando um parâmetro de matriz é usado como um parâmetro [out, retval], o parâmetro de comprimento da matriz deve ser um out parâmetro, ou seja, somente o estilo ReceiveArray é legal para retval matrizes.

Parâmetro de comprimento da matriz

Um parâmetro de matriz de estilo FillArray pode, opcionalmente, especificar outro parâmetro como o parâmetro de comprimento da matriz. Quando o parâmetro de tamanho de matriz necessário especifica o número máximo de elementos em uma matriz fornecida pelo chamador, o parâmetro de comprimento da matriz especifica o número de elementos que foram realmente preenchidos pelo receptor.

O parâmetro de comprimento da matriz é especificado com o atributo LengthIs no parâmetro de matriz.

Sobrecarga de método

Dentro do escopo de uma única interface, mais de um método pode ter o mesmo nome. Métodos com o mesmo nome em uma interface devem ter assinaturas exclusivas. Propriedades e eventos não podem ser sobrecarregados.

O WinRT dá suporte à sobrecarga em tipos de parâmetro, mas favorece a sobrecarga no número de parâmetros de entrada, também conhecido como aridade do método. Isso é feito para dar suporte a linguagens dinâmicas de tipo fraco (como JavaScript).

Quando uma interface tem vários métodos de mesmo nome e número de parâmetros de entrada, exatamente um desses métodos deve ser marcado como o padrão. De todos os métodos sobrecarregados com o mesmo nome e número de parâmetros de entrada, somente o método marcado como padrão será projetado por uma linguagem dinâmica e fracamente tipada. Se houver apenas um único método sobrecarregado de um determinado nome e número de parâmetros de entrada, marcá-lo como o padrão será válido, mas não necessário.

Para determinar a aridade de um método, os parâmetros de matriz e o parâmetro de comprimento necessário são considerados um único parâmetro. Os estilos PassArray e FillArray são considerados um único parâmetro de entrada, enquanto o estilo ReceiveArray é considerado um único parâmetro de saída.

Quando vários métodos em uma interface têm o mesmo nome, um nome exclusivo para cada método de colisão deve ser armazenado em um OverloadAttribute anexado ao método. Os métodos sobrecarregados padrão carregam o DefaultOverloadAttribute.

Sobrecarga de operador

O WinRT não dá suporte à sobrecarga do operador. Os métodos podem não ser nomeados usando os nomes de operadores especiais, como op_Addition especificados na especificação da CLI do ECMA 335, partição I, seção 10.3.

Propriedades

Uma propriedade é um par de métodos get/set com nome e tipo correspondentes que aparecem em algumas projeções de linguagem como campos em vez de métodos.

Uma propriedade e seus métodos get/set devem ter visibilidade pública.

Uma propriedade deve ter um get método. Um método getter de propriedade não tem parâmetros e retorna um valor do tipo de propriedade. Uma propriedade com apenas um get método é chamada de propriedade somente leitura.

Opcionalmente, uma propriedade pode ter um set método. Um método setter de propriedade tem um único parâmetro do tipo de propriedade e retorna void. Uma propriedade com um método e set um get método é chamada de propriedade de leitura/gravação.

As propriedades podem não ser parametrizadas. Propriedades de interfaces parametrizadas podem usar parâmetros de tipo do tipo que contém como o tipo de propriedade.

Eventos

Um evento é um par de métodos de ouvinte add/remove com o nome correspondente e o tipo de delegado. Os eventos são um mecanismo para a interface notificar as partes interessadas quando algo importante acontece.

Um evento e seus métodos de ouvinte de adição/remoção devem ter visibilidade pública.

Um método ouvinte de eventos add tem um único parâmetro do tipo delegado de evento e retorna um Windows. Foundation.EventRegistrationToken. Um método ouvinte de eventos remove tem um único parâmetro do Windows. Tipo Foundation.EventRegistrationToken e retorna void.

Os eventos podem não ser parametrizados. Eventos de interfaces parametrizadas podem usar parâmetros de tipo do tipo que contém como o tipo delegado de evento.

classes de runtime

O WinRT permite que você defina uma classe. Uma classe deve implementar uma ou mais interfaces. Uma classe não pode implementar membros de tipo diretamente (ou seja, não pode definir seus próprios métodos, propriedades nem eventos). Uma classe deve fornecer uma implementação de todos os membros de todas as interfaces que implementa.

Há vários tipos diferentes de interfaces, que são descritos em detalhes abaixo.

  • Interfaces de membro (incluindo interfaces protegidas e substituíveis)
  • Interfaces estáticas
  • Interfaces de fábrica de ativação
  • Interfaces de fábrica de composição

Classes de runtime não podem ser parametrizadas. Uma classe de runtime pode implementar uma instância de interface parametrizada (ou seja, uma interface parametrizada com todos os parâmetros de tipo especificados) em qualquer lugar que normalmente aceitasse uma interface não parametrizada.

Uma classe de runtime deve ter visibilidade pública.

Uma classe de runtime só pode implementar interfaces que não são exclusivas (ou seja, não carregam o atributo exclusiveTo) ou que são exclusivas para a classe de runtime em questão. Uma classe de runtime pode não implementar interfaces exclusivas para uma classe de runtime diferente. Há uma exceção a essa regra: uma classe composável pode implementar interfaces exclusivas a uma classe em sua cadeia de derivação que é marcada como substituível. Detalhes sobre interfaces substituíveis a seguir.

Interface do membro

Uma classe de runtime pode implementar zero ou mais interfaces de membro. As interfaces de membro permitem que as classes exponham a funcionalidade associada a instâncias da classe. Uma classe de runtime especifica uma lista das interfaces de membro que ela implementa. As entradas na lista de interfaces de membro implementadas por uma classe de runtime podem, opcionalmente, levar informações de versão. Detalhes sobre o controle de versão de classe de runtime a seguir.

As interfaces de membro são implementadas diretamente em instâncias da classe runtime.

Classes de runtime que implementam uma ou mais interfaces de membro devem especificar uma das interfaces de membro para ser a interface padrão. Classes de runtime que implementam interfaces de membro zero não especificam uma interface padrão.

Interfaces estáticas

As classes WinRT podem especificar zero ou mais interfaces estáticas. As interfaces estáticas permitem que as classes exponham a funcionalidade associada à própria classe, em vez de a instâncias específicas da classe.

Uma classe deve especificar pelo menos um membro ou interface estática. Uma classe sem membro e sem interfaces estáticas é inválida.

Interfaces estáticas são especificadas por meio de um StaticAttribute associado à classe runtime. StaticAttribute carrega uma referência da referência de interface estática, bem como informações de versão. Detalhes sobre o controle de versão de classe de runtime a seguir.

Embora as interfaces estáticas sejam declaradas como parte da classe de runtime, elas não são implementadas em instâncias de classe. Em vez disso, eles são implementados na fábrica de ativação da classe. Detalhes sobre as fábricas de ativação a seguir.

Ativação

As classes de runtime dão suporte opcionalmente à ativação— a capacidade do sistema de produzir instâncias de uma classe especificada. As classes devem implementar pelo menos uma interface de membro para dar suporte à ativação.

O WinRT define três mecanismos de ativação: ativação direta (sem parâmetros de construtor), ativação de fábrica (com um ou mais parâmetros de construtor) e ativação de composição. Classes não composáveis podem dar suporte à ativação direta e/ou de fábrica. Classes composable só dão suporte à ativação composable. Detalhes sobre a composição e a ativação redimensível a seguir.

Classes que dão suporte à ativação direta são ativadas chamando o método IActivationFactory.ActivateInstance na fábrica de ativação da classe. Esse método não usa parâmetros e retorna uma instância recém-ativada da classe runtime. Detalhes sobre as fábricas de ativação a seguir.

Classes que dão suporte à ativação de fábrica definem uma ou mais interfaces de fábrica, que por sua vez definem um ou mais métodos de fábrica. Essas interfaces de fábrica são implementadas na fábrica de ativação da classe.

Os métodos de fábrica tomam um ou mais in parâmetros e devem retornar uma instância recém-ativada da classe de runtime. Outros out parâmetros além da instância de classe recém-ativada não são permitidos. Os métodos de fábrica devem usar um ou mais parâmetros — a ativação de fábrica sem parâmetros não é permitida. A ativação direta deve ser usada para ativação sem parâmetros.

Classes que dão suporte à ativação direta ou de fábrica são marcadas com o ActivatableAttribute. O ActivatableAttribute carrega informações de versão (detalhes sobre o controle de versão de runtime a seguir), bem como uma referência opcional à interface de fábrica. As classes podem ser marcadas com vários ActivatableAttributes , no máximo um para ativação padrão, além de uma para cada interface de fábrica implementada pela fábrica de ativação da classe. Classes marcadas com o ActivatableAttribute também podem não ser marcadas com o ComposableAttribute. Detalhes sobre a composição a seguir.

Composição

As classes de runtime dão suporte opcionalmente à composição, a capacidade de várias instâncias de classe serem combinadas no que parece ser um único objeto de fora. O WinRT usa a composição como uma forma de herança de classe de runtime.

As classes WinRT podem, opcionalmente, compor uma única classe base composável, que, por sua vez, pode compor uma única classe base composável, etc. Uma classe em si não precisa ser redigida para compor uma classe base composável. As classes só podem compor com uma classe composable como uma classe base. Uma classe composable não é necessária para compor outra classe composable (ou seja, pode ser a raiz da hierarquia). Grafos circulares de composição (como um composes B, que compõe A) não são permitidos.

Em runtime, uma classe de composição é uma agregação de objetos WinRT— um para cada objeto na cadeia de composição. Esses objetos agregados delegam identidade e tempo de vida ao objeto originalmente ativado na cadeia de composição (chamado de objeto controlador). Cada objeto na cadeia contém um ponteiro IInspectable não delegante para a classe que ele compõe para chamar métodos em interfaces de classe base compostas, incluindo métodos em interfaces protegidas. Cada objeto na cadeia tem um ponteiro para a classe controladora para delegar tempo de vida e identidade, bem como para chamar métodos em interfaces substituíveis. Detalhes sobre interfaces protegidas e substituíveis a seguir.

Vamos dar o exemplo de que o Botão compõe o Controle, que, por sua vez, compõe o UIElement. Nesse exemplo, uma instância de Button agrega uma instância de Controle , que, por sua vez, agrega uma instância UIElement . Todos os três objetos têm uma referência ao objeto Button para controlar o tempo de vida e a identidade, bem como para consultar interfaces substituíveis. Cada objeto tem um ponteiro IInspectable para o objeto que ele compõe (Button contém um ponteiro para Control; O controle contém um ponteiro para UIElement) para poder chamar métodos em interfaces implementadas em classes compostas, incluindo interfaces protegidas.

Uma classe pode não implementar nenhuma interface definida na classe que compõe, nem qualquer classe na cadeia de composição, a menos que a interface seja marcada como substituível na classe composável. Detalhes sobre interfaces substituíveis a seguir.

Uma classe composable deve ser marcada com um ou mais ComposableAttributes. O ComposableAttribute carrega uma referência à interface de fábrica de composição, se os métodos de fábrica na interface de fábrica de composição podem ser usados para controlar a ativação de objeto ou não, bem como informações de versão. Detalhes sobre interfaces de fábrica de composição e controle de versão a seguir. Uma classe pode ser marcada com vários ComposableAttributes — um para cada interface de fábrica de composição implementada pelo fábrica de ativação da classe.

A projeção de linguagem JavaScript não dá suporte à composição de classe. Dessa forma, classes composable e classes que compõem classes composable devem ser marcadas com o WebHostHiddenAttribute indicando que JavaScript não deve tentar projetar esses tipos.

Terceiros só podem definir classes que compõem outras classes redigidas. Talvez você não defina sua própria classe raiz composável.

Ativação composável

Uma classe composável deve definir uma ou mais interfaces de fábrica de composição, que, por sua vez, implementam um ou mais métodos de fábrica de composição. As interfaces de fábrica de composição são implementadas na fábrica de ativação da classe. Detalhes sobre as fábricas de ativação a seguir.

Uma interface de fábrica de composição é usada para criar instâncias composáveis da classe. Uma interface de fábrica composável declara zero ou mais métodos de fábrica redigidos que podem ser usados para ativar instâncias da classe para fins de composição. Observe que é legal ter uma interface de fábrica composável com métodos de fábrica zero. Isso implica que a classe pode ser usada para composição, mas que terceiros podem não compor diretamente a classe— os métodos para criar instâncias são somente internos.

Uma classe composável declara se os métodos de fábrica em uma determinada interface de fábrica de composição podem ser usados para ativar a classe diretamente como um objeto controlador ou não. As interfaces de fábrica composáveis marcadas como públicas podem ser usadas para ativar diretamente uma classe como um objeto controlador, bem como indiretamente para ativar uma classe como um objeto composto. Interfaces de fábrica composable marcadas como protegidas só podem ser usadas para ativar indiretamente uma classe como um objeto composto. Classes composáveis sempre podem ser ativadas como objetos compostos.

Uma interface de fábrica de composição deve ser exclusiveto a classe de runtime pela qual ela é implementada.

Como um método de fábrica de ativação, um método de fábrica de composição deve retornar uma instância da classe composável. Além disso, um método de fábrica de composição tem dois parâmetros adicionais: o parâmetro IInspectable* [in] controlador e o parâmetro IInspectable** [out]. Opcionalmente, um método de fábrica de composição pode ter parâmetros adicionais in . Se especificado, o adicional nos parâmetros deve ocorrer no início da assinatura do método, antes dos parâmetros obrigatórios listados anteriormente. Um método de fábrica de composição pode não ter parâmetros adicionais out além do IInspectable não delegante** e dos parâmetros de valor retornado.

Quando uma classe composável é ativada para composição (por exemplo, Controle ou UIElement quando uma instância de botão é ativada), um ponteiro para o objeto que controla a identidade e o tempo de vida é passado por meio do parâmetro IInspectable* [in]. O método de fábrica composável retorna a instância recém-ativada como o valor retornado. Essa instância delega toda a funcionalidade de gerenciamento de identidade e tempo de vida para o controle IInspectable* fornecido. Além disso, o método de fábrica composable retorna um ponteiro para um IInspectable não delegante* que a classe de composição pode usar para invocar métodos em uma classe composta.

Quando uma classe composable é ativada como uma classe de controle (por exemplo, Button no exemplo anterior), os mesmos métodos de fábrica redigiráveis são usados como para ativação para composição. Ao ativar uma classe redigível diretamente, nulo é passado para o parâmetro *IInspectable* controlador. Esse é um indicador para a classe composável que está sendo ativada como uma classe controladora. Quando uma classe de controle cria a instância da classe que compõe, ela passa uma referência a si mesma como o parâmetro IInspectable* controlador. O método de fábrica composável retorna a instância de classe de controle como o valor retornado. O parâmetro IInspectable** [out] não delegante é ignorado pelo código do cliente ao ativar uma classe composável de controle.

Com base no exemplo anterior de Button ->Control ->UIElement , a classe de botão estaria ativando chamando um de seus métodos de fábrica de composição e passando nulo para o parâmetro externo. O botão ativaria, por sua vez, uma instância de Controle , passando uma referência para si mesma como o parâmetro externo. Por sua vez, o controle ativaria uma instância UIElement , passando a referência externa recebida como o parâmetro externo. O método de fábrica UIElement retornaria ao Controle do UIElement recém-criado no parâmetro de instância, bem como uma referência ao IInspectable não delegante do UIElement no parâmetro interno. O método control factory retornaria a Button o controle recém-criado no parâmetro de instância, bem como uma referência ao IInspectable não delegante do Control no parâmetro interno. A fábrica de composição button retornaria ao código de chamada o botão recém-criado no parâmetro de instância e nulo para o parâmetro interno.

Às vezes, é possível que uma classe seja ativada para composição e outras vezes ativada como a classe controladora. Por exemplo, se o Botão composto por RadioButton, o Button seria ativado para composição quando um RadioButton fosse ativado; mas ativado como a classe de controle quando Button foi ativado diretamente. Em ambos os casos, a classe Control que Button compõe seria ativada para composição.

Interfaces protegidas

Uma classe composável pode declarar zero ou mais de suas interfaces de membro a serem protegidas. Uma classe não composável pode não declarar interfaces de membro a serem protegidas. Somente o código em uma classe que compõe uma classe composável (direta ou indiretamente) pode consultar e usar interfaces que a classe composável declara como protegida. O código de fora da cadeia de composição pode não consultar, nem usar, interfaces que a classe composável declara como protegidas.

Por exemplo, se UIElement declarar uma interface protegida IUIElementProtected, somente classes que compõem UIElement, incluindo a composição direta (Controle) e indireta (Botão), poderão consultar e usar a interface IUIElementProtected .

Interfaces substituíveis

Uma classe composável pode declarar zero ou mais de suas interfaces de membro como substituíveis. Uma interface substituível só pode ser consultada e usada dentro de uma cadeia de composição, semelhante às regras sobre como acessar interfaces protegidas detalhadas anteriormente. No entanto, quando uma interface protegida só pode ser implementada pela classe que a declarou originalmente, as interfaces substituíveis podem ser reimplantes por classes que compõem a classe que implementou a interface substituível.

Em runtime, qualquer código de classe que aproveite a interface substituível deve queryInterface para a interface por meio do ponteiro IInspectable* que é usado para delegação de identidade e tempo de vida. Esse ponteiro retorna a implementação da interface substituível mais cedo na cadeia de composição (ou seja, mais próxima da instância de classe de controle). Uma classe que deseja acessar as interfaces substituíveis da classe que ela compõe pode fazer isso por meio da referência não delegada que uma classe composável contém à sua classe composta.

Vamos dar o exemplo de que UIElement declara uma interface IUIElementOverridable substituível. Nesse caso, classes que derivam de UIElement, incluindo derivação direta (Controle) e indireta (Botão), teriam permissão para implementá-la. Se o código em UIElement fosse necessário para acessar a funcionalidade em IUIElementOverridable, uiElement consultaria o IInspectable controlador para obter a implementação mais antiga na cadeia de composição. Se Control e Button implementassem IUIElementOverridable, a implementação do Botão seria retornada quando o controle IInspectable fosse consultado. Se Button quiser acessar sua funcionalidade de classe composta, ele poderá usar o IInspectable não delegante que foi retornado do método de fábrica de composição para consultar a classe base para essa interface.

Alocadores de ativação

Opcionalmente, uma classe de runtime tem uma fábrica de ativação. Uma classe de runtime deve ter uma fábrica de ativação se a classe for ativada, composável ou tiver interfaces estáticas. A fábrica de ativação de uma classe pode ser recuperada do sistema em runtime por meio da função RoGetActivationFactory Win32.

As Fábricas de Ativação devem implementar a interface IActivationFactory . No entanto, somente as classes que dão suporte à ativação direta fornecem uma implementação do método único do IActivationFactoryActivateInstance. Classes que não dão suporte à ativação direta devem retornar E_NOTIMPL de IActivationFactory.ActivateInstance.

A fábrica de ativação deve implementar todas as interfaces de fábrica de ativação, interfaces de fábrica de composição e interfaces estáticas definidas na classe de runtime.

Não há garantia de que as projeções de idioma mantenham uma única instância de fábrica de ativação para o tempo de vida da fábrica. Os autores da classe WinRT que precisam salvar informações de longa duração para acesso a membros estáticos precisam armazená-los em algum lugar fora da fábrica de ativação.

Projeção baseada em classe

Embora o WinRT seja principalmente um modelo de programação baseado em interface sob o capô, as classes de runtime fornecem um modelo de programação baseado em classe que é melhor alinhado às linguagens de programação modernas, tradicionais e orientadas a objetos (OO). Espera-se que as projeções de linguagem projetem uma classe de runtime como uma única entidade, em vez de como um recipiente de interfaces com as quais o desenvolvedor precisa lidar separadamente.

Para alcançar esse modelo baseado em classe, espera-se que as projeções de linguagem projecionem membros do tipo das interfaces de membro de uma classe como membros de classe direta. Espera-se que as projeções de linguagem projetem membros do tipo das interfaces estáticas de uma classe como membros de classe estáticos. Por fim, espera-se que as projeções de linguagem projecionem métodos de ativação (ativação direta, bem como interfaces de fábrica e interfaces de fábrica composáveis) como construtores de classe.

Para ajudar nessa projeção baseada em classe, os metadados para classes de runtime especificam um membro de classe para todos os métodos, propriedades e eventos de cada interface que implementam. Cada membro de classe é explicitamente vinculado ao membro da interface onde ele foi originalmente definido. Isso permite que as projeções de linguagem exponham a classe como uma única entidade, tratando todas as consultas de interface e a contagem de referências sob as capas em nome do desenvolvedor.

Por padrão, cada membro de interface implementado é projetado como um membro de classe. No entanto, como as classes de runtime podem implementar várias interfaces independentes e versão ao longo do tempo (detalhes de controle de versão a seguir), é possível que haja colisões de nome para membros definidos em diferentes interfaces implementadas por uma única classe de runtime.

Quando ocorrem colisões, a projeção de membro de classe padrão é impossível. Se ocorrerem colisões entre interfaces adicionadas em versões separadas, o membro em colisão da versão mais antiga será projetado como um membro de classe. Quando ocorrem colisões entre interfaces adicionadas na mesma versão, nenhum dos membros em colisão é projetado como membros da classe. Observe que os métodos com nomes de colisão são permitidos desde que todas as versões sejam de aridade diferente, conforme descrito na sobrecarga de método.

Os membros de interface que não são projetados como membros da classe devem ser disponibilizados aos desenvolvedores. Normalmente, isso é feito por um operador de pesquisa dinâmica ou de conversão, permitindo que o desenvolvedor especifique a interface e o método específicos que deseja invocar.

Para resolver colisões de nome de método, as classes de runtime podem especificar nomes alternativos para métodos nas interfaces estáticas e membro que implementam. Esse nome alternativo é usado pela projeção de linguagem para fornecer acesso desambiguado a nomes de método de colisão de uma instância de classe. Embora a classe de runtime possa fornecer um nome de método alternativo, a assinatura do método, os parâmetros e quaisquer atributos anexados ao método ou seus atributos ainda devem corresponder exatamente à definição da interface original.

Como a ativação direta, os métodos de fábrica e os métodos de fábrica de composição são projetados como construtores de classe, todos eles são projetados na classe de runtime como se tivessem o mesmo nome. Todos os métodos em todas as interfaces de fábrica devem ter assinaturas exclusivas, devem favorecer a sobrecarga baseada em arity sobre sobrecarga baseada em tipo e devem usar o DefaultOverloadAttribute para desambiguar métodos de fábrica da mesma aridade.

Controle de versão de classe

As classes de runtime são aditivamente versões. As versões subsequentes de uma determinada classe de runtime podem especificar interfaces adicionais de todos os tipos, com mais detalhes sobre tipos de interface individuais abaixo. As interfaces pré-existentes especificadas por uma classe podem nunca ser removidas ou alteradas sem quebrar a compatibilidade com versões anteriores.

Controle de versão da interface do membro

As interfaces de membro em classes de runtime são aditivamente versões. As versões subsequentes de uma determinada classe de runtime podem implementar interfaces de membro adicionais, mesmo que a classe nunca tivesse implementado interfaces de membro anteriormente. As versões subsequentes de uma determinada classe de runtime composable podem implementar interfaces adicionais protegidas e substituíveis.

As interfaces implementadas por uma classe de runtime opcionalmente carregam o VersionAttribute para distinguir quando interfaces específicas foram adicionadas ao tipo de classe de runtime. Os valores de implementação da interface sem um VersionAttribute são considerados com o mesmo valor de versão que o tipo de classe de runtime que inclui.

Controle de versão da interface estática

Interfaces estáticas em classes de runtime são aditivamente versões. As versões subsequentes de uma determinada classe de runtime podem implementar interfaces estáticas adicionais, mesmo que a classe nunca tivesse implementado interfaces estáticas anteriormente.

O StaticAttribute inclui um parâmetro UInt32 para número de versão, que define a versão do Windows que adicionou esse suporte à ativação.

Controle de versão de ativação

O suporte à ativação para classes de runtime é aditivamente versionável. As versões subsequentes de uma determinada classe de runtime podem implementar mecanismos de ativação adicionais, mesmo que a classe nunca tenha implementado um mecanismo de ativação. Observe que as classes composable não são ativadas e, portanto, podem não adicionar suporte à ativação.

Observe que uma classe que dá suporte à ativação direta pode adicionar apenas novas interfaces de ativação de fábrica. Uma classe que anteriormente só tinha suporte para ativação de fábrica pode adicionar suporte à ativação direta, bem como novas interfaces de ativação de fábrica.

O ActivatableAttribute inclui um parâmetro UInt32 para o número da versão. O número da versão do ActivatableAttribute define a versão do Windows que adicionou esse suporte à ativação.

Controle de versão de composição

O suporte à composição para classes de runtime é aditivamente versionável. As versões subsequentes de uma determinada classe de runtime composable podem implementar mecanismos de composição adicionais, desde que a classe tenha sido definida como composável quando foi criada. Classes composable podem não adicionar suporte à ativação.

O ComposableAttribute inclui um parâmetro UInt32 para o número da versão. O número da versão do ComposableAttribute define a versão do Windows que adicionou esse suporte à composição.

Atributos personalizados

O WinRT dá suporte à definição de atributos de metadados personalizados. Todos os constructos no sistema de tipos WinRT podem levar atributos de metadados personalizados. Isso inclui todos os tipos nomeados (enumerações, structs, delegados, interfaces, classes etc.), bem como elementos individuais contidos em construções de tipo (como métodos, parâmetros etc.).

Atributos personalizados são nomeados como outros tipos WinRT. No entanto, eles não são ativados. Eles são puramente uma construção de dados.

Atributos personalizados definem um esquema de dados de parâmetros posicionais ou campos nomeados. Um atributo personalizado pode não usar parâmetros posicionais e campos nomeados. Ele deve escolher um ou outro. Os tipos de parâmetros e campos de um atributo personalizado são limitados aos tipos fundamentais do WinRT, enumerações e referências a outros tipos WinRT. Nenhum outro parâmetro ou tipo de campo é permitido.

Atributos personalizados que usam parâmetros posicionais devem definir um ou mais conjuntos válidos de parâmetros posicionais. Cada conjunto deve especificar zero ou mais parâmetros posicionais. Uma instância do atributo personalizado deve especificar um único conjunto de parâmetros posicionais, bem como dados para cada parâmetro posicional no conjunto selecionado.

Um atributo personalizado que usa campos nomeados especifica zero campos com nomes e tipos. Uma instância do atributo personalizado deve especificar os pares nome/valor para os campos que deseja especificar. Uma instância pode especificar valores para todos, alguns ou nenhum dos pares nome/valor.

É válido que um atributo não tenha parâmetros posicionais nem campos nomeados.

Um atributo personalizado deve ter visibilidade pública.

Um atributo pode especificar os tipos de builds de tipo WinRT aos quais ele pode ser associado por meio do AttributeUsageAttribute.

Terceiros não podem definir atributos personalizados. Há suporte apenas para os atributos personalizados definidos pelo sistema.