Partilhar via


Permitir o uso da diretiva alias para fazer referência a qualquer tipo de tipo

Observação

Este artigo é uma especificação de recurso. A especificação serve como o documento de design para o recurso. Ele inclui mudanças de especificação propostas, juntamente com as informações necessárias durante o design e desenvolvimento do recurso. Estes artigos são publicados até que as alterações de especificações propostas sejam finalizadas e incorporadas na especificação ECMA atual.

Pode haver algumas discrepâncias entre a especificação do recurso e a implementação concluída. Essas diferenças são capturadas nas notas pertinentes da Reunião de Design de Linguagem (LDM) .

Você pode saber mais sobre o processo de adoção de especificações de recursos no padrão de linguagem C# no artigo sobre as especificações .

Questão campeã: https://github.com/dotnet/csharplang/issues/8645

Resumo

Relaxe a diretiva using_alias_directive (§13.5.2) para permitir que ela aponte para qualquer tipo, e não apenas para os tipos nomeados. Isso suportaria tipos não permitidos hoje, como: tipos de tupla, tipos de ponteiro, tipos de matriz, etc. Por exemplo, isto passaria a ser permitido:

using Point = (int x, int y);

Motivação

Por muito tempo, o C# teve a capacidade de introduzir aliases para namespaces e tipos nomeados (classes, delegados, interfaces, registros e structs). Isso funcionou aceitavelmente bem, pois forneceu meio de introduzir nomes não conflitantes nos casos em que um nome normal obtido de using_directives pode ser ambíguo, e permitiu uma maneira de fornecer um nome mais simples ao lidar com tipos genéricos complexos. No entanto, o surgimento de símbolos de tipo complexos adicionais na língua fez com que surgisse mais uso onde os aliases seriam valiosos, mas atualmente não são permitidos. Por exemplo, tanto as tuplas quanto os ponteiros de função muitas vezes podem ter formas textuais regulares grandes e complexas que podem ser dolorosas de se escrever repetidamente e difíceis de ler. Os aliases ajudariam nesses casos, dando um nome curto, fornecido pelo desenvolvedor, que pode ser usado no lugar dessas formas estruturais completas.

Projeto detalhado

Vamos mudar a gramática de using_alias_directive assim:

using_alias_directive
-    : 'using' identifier '=' namespace_or_type_name ';'
+    : 'using' identifier '=' (namespace_name | type) ';'
    ;

Anotações de anulabilidade em tipos de referência de nível superior não são permitidas.

Curiosamente, a maior parte da linguagem de especificação no §13.5.2 não precisa mudar. A maioria da linguagem já refere-se a 'namespace ou tipo', por exemplo:

Um using_alias_directive introduz um identificador que serve como um pseudónimo para um namespace ou tipo dentro do corpo de namespace ou unidade de compilação que o encerra imediatamente.

Isso continua sendo verdade, apenas que a gramática agora permite que o "tipo" seja qualquer tipo arbitrário, não o conjunto limitado permitido por namespace_or_type_name anteriormente.

As seções que precisam ser atualizadas são:

- The order in which using_alias_directives are written has no significance, and resolution of the namespace_or_type_name referenced by a using_alias_directive is not affected by the using_alias_directive itself or by other using_directives in the immediately containing compilation unit or namespace body. In other words, the namespace_or_type_name of a using_alias_directive is resolved as if the immediately containing compilation unit or namespace body had no using_directives. A using_alias_directive may however be affected by extern_alias_directives in the immediately containing compilation unit or namespace body. In the example
+ The order in which using_alias_directives are written has no significance, and resolution of the `(namespace_name | type)` referenced by a using_alias_directive is not affected by the using_alias_directive itself or by other using_directives in the immediately containing compilation unit or namespace body. In other words, the `(namespace_name | type)` of a using_alias_directive is resolved as if the immediately containing compilation unit or namespace body had no using_directives. A using_alias_directive may however be affected by extern_alias_directives in the immediately containing compilation unit or namespace body. In the example
- The namespace_name referenced by a using_namespace_directive is resolved in the same way as the namespace_or_type_name referenced by a using_alias_directive. Thus, using_namespace_directives in the same compilation unit or namespace body do not affect each other and can be written in any order.
+ The namespace_name referenced by a using_namespace_directive is resolved in the same way as the namespace_or_type_name referenced by a using_alias_directive. Thus, using_namespace_directives in the same compilation unit or namespace body do not affect each other and can be written in any order.
+ It is illegal for a using alias type to be a nullable reference type.

    1. `using X = string?;` is not legal.
    2. `using X = List<string?>;` is legal.  The alias is to `List<...>` which is itself not a nullable reference type itself, even though it contains one as a type argument.
    3. `using X = int?;` is legal.  This is a nullable *value* type, not a nullable *reference* type.

Suportar aliases para tipos que contêm ponteiros.

Uma nova de contexto insegura é adicionada através de uma palavra-chave opcional "não seguro" na produção using_alias_directive:

using_alias_directive
+    : 'using' 'unsafe'? identifier '=' (namespace_name | type) ';'
    ;
    
using_static_directive
+    : 'using' 'static' 'unsafe'? type_name ';'
    ;

+ 'unsafe' can only be used with an using_alias_directive or using_static_directive, not a using_directive.
+ The 'unsafe' keyword present in a 'using_alias_directive' causes the entire textual extent of the 'type' portion (not the 'namespace_name' portion) to become an unsafe context. 
+ The 'unsafe' keyword present in a 'using_static_directive' causes the entire textual extent of the 'type_name' portion to become an unsafe context.