允许使用别名指令引用任何类型的类型

注意

本文是特性规范。 该规范充当该功能的设计文档。 它包括建议的规范更改,以及功能设计和开发过程中所需的信息。 这些文章将发布,直到建议的规范更改最终确定并合并到当前的 ECMA 规范中。

功能规范与已完成的实现之间可能存在一些差异。 这些差异记录在相关的语言设计会议 (LDM) 说明中。

可以在 规范一文中详细了解将功能规范采用 C# 语言标准的过程。

支持者问题:https://github.com/dotnet/csharplang/issues/8645

总结

放宽 using_alias_directive (§13.5.2) 以允许它指向任何类型,而不仅仅是命名类型。 这将支持今天不允许的类型,例如:元组类型、指针类型、数组类型等。例如,现在允许这样做:

using Point = (int x, int y);

动机

长期以来,C# 已经具备为命名空间和命名类型(类、委托、接口、记录和结构)引入别名的能力。 这种方法运行效果尚可,因为它提供了一种引入无冲突名称的方法,在某些情况下,从 using_directive拉取的正常名称可能会引发歧义。此外,它允许在处理复杂的泛型类型时提供一个更简单的名称。 但是,语言中其他复杂类型符号的兴起导致出现更多的使用,因为别名会很有价值,但目前不允许使用。 例如,元组和函数指针通常都有庞大而复杂的规则文本形式,连续写出这些形式可能很痛苦,而且阅读起来也很负担。 别名可以帮助在这些情况下提供一个简短的、由开发人员提供的名称,这个名称可以用来替代那些完整的结构形式。

详细设计

因此,我们将更改 using_alias_directive 的语法:

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

不允许顶级引用类型的可为 Null 性注释。

有趣的是,§13.5.2 中的大多数规范语言不需要更改。 其中的大部分语言都已引用“命名空间或类型”,例如:

using_alias_directive 引入了一个标识符,该标识符用作直接封闭的编译单元或命名空间正文中的命名空间或类型的别名。

这仍属实,只是语法现在允许“type”成为任意类型,而不是以前 namespace_or_type_name 允许的有限集。

需要更新的部分包括:

- 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.

支持包含指针的类型别名。

在 using_alias_directive 生成中,通过可选的“unsafe”关键字添加了一个新的不安全上下文

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.