全局 Using 指令

备注

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

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

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

using 指令的语法扩展为可选的global关键字,该关键字位于 using 关键字前面:

compilation_unit
    : extern_alias_directive* global_using_directive* using_directive* global_attributes? namespace_member_declaration*
    ;

global_using_directive
    : global_using_alias_directive
    | global_using_namespace_directive
    | global_using_static_directive
    ;

global_using_alias_directive
    : 'global' 'using' identifier '=' namespace_or_type_name ';'
    ;

global_using_namespace_directive
    : 'global' 'using' namespace_name ';'
    ;
    
global_using_static_directive
    : 'global' 'using' 'static' type_name ';'
    ;
  • global_using_directive 只允许在编译单元级别(不能在 namespace_declaration 内使用)。
  • global_using_directive(如有)必须位于任何 using_directive 之前。
  • global_using_directive 的范围扩展到程序内所有编译单元 namespace_member_declaration 范围。 具体来说,global_using_directive 的范围不包括其他 global_using_directive。 因此,对等 global_using_directive 或不同编译单元的指令不会相互影响,且它们的编写顺序也无关紧要。 具体来说,global_using_directive 的范围不包括直接包含在程序任何编译单元中的 using_directive

global_using_directive 添加到程序中的效果,可以被视为将一个解析为相同目标命名空间或类型的类似 using_directive 添加到程序每个编译单元中的效果。 但是,在包含 global_using_directive 的编译单元的上下文中,global_using_directive 的目标被解析。

§7.7 范围

这些是相关要点及建议补充(以粗体显示):

  • extern_alias_directive 定义的名称范围扩展到 global_using_directiveusing_directiveglobal_attributesnamespace_member_declaration的立即包含编译单元或命名空间正文。 extern_alias_directive 不向底层声明空间贡献任何新成员。 换句话说,extern_alias_directive 不是传递性的,而是只影响其所在的编译单元或命名空间的正文。
  • global_using_directive 定义或导入的名称的范围覆盖了程序中所有 global_attributesnamespace_member_declarationcompilation_unit

§7.8 命名空间和类型名称

对确定 namespace_or_type_name 含义的算法进行了更改,如下所示。

这是相关的项目符号,附有建议添加的内容(以粗体显示):

  • 如果 namespace_or_type_name 是形式 I 或形式 I<A1, ..., Ak>
    • 如果 K 为零,并且 namespace_or_type_name 出现在泛型方法声明(§15.6)中;如果该声明包含名称 I的类型参数(§15.2.3),则 namespace_or_type_name 引用该类型参数。
    • 否则,如果 namespace_or_type_name 出现在类型声明中,则对于每个实例类型 T(§15.3.2),从该类型声明的实例类型开始,接着处理每个外包类或结构体声明的实例类型(如有):
      • 如果 K 为零,并且 T 声明包含名称为 I的类型参数,则 namespace_or_type_name 引用该类型参数。
      • 否则,如果 namespace_or_type_name 出现在类型声明的正文中,并且 T 或其任何基类型都包含具有名称 IK 类型参数的嵌套可访问类型,则 namespace_or_type_name 引用使用给定类型参数构造的类型。 如果有多个此类类型,则会选择在更多派生类型中声明的类型。 请注意,确定 namespace_or_type_name的含义时,将忽略非类型成员(常量、字段、方法、属性、索引器、运算符、实例构造函数、析构函数和静态构造函数)以及具有不同数量类型参数的类型成员。
    • 如果之前的步骤没有成功,则对于每个命名空间 N,从 namespace_or_type_name 所在的命名空间开始,然后继续检查每个包含的命名空间(如有),以全局命名空间结尾,以下步骤将被评估,直到找到实体为止:
      • 如果 K 为零,IN中的命名空间的名称,则:
        • 如果发生 namespace_or_type_name 的位置由 N 命名空间声明括起来,并且命名空间声明包含 extern_alias_directiveusing_alias_directive,该名称 I 与命名空间或类型相关联,则 或程序中 N 的任何命名空间声明包含将名称 I 与命名空间或类型关联的 global_using_alias_directivenamespace_or_type_name 不明确,并出现编译时错误。
        • 否则,namespace_or_type_name 引用 I 中名为 N 的命名空间。
      • 否则,如果 N 包含名称 IK 类型参数的可访问类型,则:
        • 如果K 为 0,且发生 namespace_or_type_name 的位置由 N 命名空间声明括起来,并且命名空间声明包含 extern_alias_directiveusing_alias_directive,该名称 I 与命名空间或类型相关联,则 或程序中 N 的任何命名空间声明包含将名称 I 与命名空间或类型关联的 global_using_alias_directivenamespace_or_type_name 不明确,并出现编译时错误。
        • 否则,namespace_or_type_name 指代由给定类型参数构造的类型。
      • 否则,如果发生 namespace_or_type_name 的位置由 N的命名空间声明括起来:
        • 如果 K 为零,并且命名空间声明包含将名称 与导入的命名空间或类型关联的 extern_alias_directiveusing_alias_directiveI,则 或程序中 N 的任何命名空间声明都包含将名称 与导入命名空间或类型关联的 global_using_alias_directiveInamespace_or_type_name 引用该命名空间或类型。
        • 否则,如果命名空间和类型声明由 using_namespace_directive 和命名空间声明 using_alias_directive 导入,以及由 global_using_namespace_directive 导入的命名空间和类型声明以及程序 N 的任何命名空间声明的命名空间 global_using_static_directive 和类型声明,则仅包含名称 IK 类型参数的一种可访问类型, 然后,namespace_or_type_name 引用使用给定类型参数构造的类型。
        • 否则,如果命名空间和类型声明由 using_namespace_directive 和命名空间声明 using_alias_directive 导入,以及由 global_using_namespace_directive 导入的命名空间和类型声明以及程序 N 的任何命名空间声明的命名空间 global_using_static_directive 和类型声明,则包含名称 IK 类型参数的多种可访问类型, 然后,namespace_or_type_name 不明确,并会出现错误。
    • 否则,namespace_or_type_name 未定义且发生编译时错误。

简单名称 §12.8.4

simple_name 评估规则进行了更改,更改如下。

这是相关的项目符号,附有建议添加的内容(以粗体显示):

  • 否则,对于每个命名空间 N,从 simple_name 所在的命名空间开始,继续每个封闭的命名空间(如果有),直到全局命名空间,以下步骤将被执行,直到找到一个实体:
    • 如果 K 为零,IN中的命名空间的名称,则:
      • 如果发生 simple_name 的位置由 N 命名空间声明括起来,并且命名空间声明包含 extern_alias_directiveusing_alias_directive,该名称 I 与命名空间或类型相关联,则 或程序中 N 的任何命名空间声明包含将名称 I 与命名空间或类型关联的 global_using_alias_directivesimple_name 不明确,并出现编译时错误。
      • 否则,simple_name 指的是 N中名为 I 的命名空间。
    • 否则,如果 N 中包含一个具有名称 IK 个类型参数的可访问类型,那么:
      • 如果K为 0,并且发生 simple_name 的位置由 N 命名空间声明括起来,并且命名空间声明包含 extern_alias_directiveusing_alias_directive,该名称 I 与命名空间或类型相关联,则 或程序中 N 的任何命名空间声明包含将名称 I 与命名空间或类型关联的 global_using_alias_directivesimple_name 不明确,并出现编译时错误。
      • 否则,namespace_or_type_name 指代由给定类型参数构造的类型。
    • 否则,如果发生 simple_name 的位置由 N的命名空间声明括起来:
      • 如果 K 为零,并且命名空间声明包含将名称 与导入的命名空间或类型关联的 extern_alias_directiveusing_alias_directiveI,则 或程序中 N 的任何命名空间声明都包含将名称 与导入命名空间或类型关联的 global_using_alias_directiveIsimple_name 引用该命名空间或类型。
      • 否则,如果命名空间和类型声明由 using_static_directive 和命名空间声明 using_alias_directive 导入,以及由 global_using_namespace_directive 导入的命名空间和类型声明以及程序 N 的任何命名空间声明的命名空间 global_using_static_directive 和类型声明,则仅包含名称 IK 类型参数的一种可访问类型或非扩展静态成员,然后,simple_name 引用使用给定类型或成员构造的类型。
      • 否则,如果N程序中的命名空间声明的 using_namespace_directive导入的命名空间和类型以及任何命名空间声明的 global_using_namespace_directive global_using_static_directive 导入的命名空间和类型声明包含多个具有名称IK类​​型参数的可访问类型或非扩展方法静态成员,则 simple_name 会产生歧义,并会出现错误。

扩展方法调用 §12.8.10.3

对算法进行更改,以查找最佳 type_nameC,如下所示。 这是相关的项目符号,附有建议添加的内容(以粗体显示):

  • 从最近的封闭命名空间声明开始,依次经过每个封闭命名空间声明,最后以包含的编译单元结束,逐步尝试查找一组候选的扩展方法:
    • 如果给定的命名空间或编译单元直接包含 Ci 符合条件的扩展方法 Mj的非泛型类型声明,则这些扩展方法的集合是候选集。
    • 如果类型 Ci 是通过 的 using_static_declarations 导入,并且在由 的 using_namespace_directive导入的命名空间或编译单元 中直接声明;如果已经达到了包相关的编译单元,则类型进一步通过 的 global_using_static_declarations 导入,并且在程序中由 的 global_using_namespace_directive导入的命名空间中直接声明,这些命名空间直接包含符合条件的扩展方法 Mj,那么这些扩展方法的集合便是候选集合。

编译单元 §14.2

compilation_unit 定义源代码文件的整体结构。 编译单元由零个或多个 global_using_directive,后跟零个或多个 using_directive,后 跟零个或多个 global_attributes 后跟零个或多个 namespace_member_declaration

compilation_unit
    : extern_alias_directive* global_using_directive* using_directive* global_attributes? namespace_member_declaration*
    ;

C# 程序由一个或多个编译单元组成,每个单元都包含在单独的源文件中。 编译 C# 程序后,所有编译单元将一起处理。 因此,编译单元可以相互依赖,甚至可能形成循环依赖关系。

在一个程序中,编译单元的 global_using_directive 会影响所有编译单元的 global_attributesnamespace_member_declaration

外部别名 §14.4

extern_alias_directive 的范围扩展到 global_using_directiveusing_directiveglobal_attributesnamespace_member_declaration的立即包含编译单元或命名空间正文。

使用别名指令 §14.5.2

写入 using_alias_directive 的顺序没有意义,并且 using_alias_directive 引用的 namespace_or_type_name 的解析不受 using_alias_directive 本身或包含编译单元或命名空间正文的其他 using_directive的影响,如果编译单元中立即包含 using_alias_directive,则不受程序中的 global_using_directive 的影响。 换句话说,解析 using_alias_directivenamespace_or_type_name 时,就好像立即所在的编译单元或命名空间主体没有 using_directive 一样;并且如果 using_alias_directive 立即包含在编译单元中,程序就好像没有 global_using_directive。 但是,using_alias_directive 可能会受到立即包含编译单元或命名空间正文中的 extern_alias_directive的影响。

全局使用别名指令

global_using_alias_directive 引入了一个标识符,该标识符用作程序中命名空间或类型的别名。

global_using_alias_directive
    : 'global' 'using' identifier '=' namespace_or_type_name ';'
    ;

在包含 global_using_alias_directive的程序的任何编译单元中的成员声明中,global_using_alias_directive 引入的标识符可用于引用给定的命名空间或类型。

包含 global_using_alias_directive的程序中任意编译单元的声明空间内,global_using_alias_directive标识符必须是唯一的。

与常规成员一样,由 global_using_alias_directive 引入的名称会被嵌套作用域中同名的成员所隐藏。

global_using_alias_directive 的顺序没有特殊意义,并且 global_using_alias_directive 所引用的 namespace_or_type_name 的解析不受 global_using_alias_directive 本身或其他 global_using_directive或程序中的 using_directive的影响。 换句话说,解析 global_using_alias_directivenamespace_or_type_name,仿佛直接包含的编译单元没有 using_directive,并且整个包含的程序没有 global_using_directive。 但是,global_using_alias_directive 可能会受到立即包含编译单元的 extern_alias_directive 的影响。

global_using_alias_directive 可以为任何命名空间或类型创建别名。

通过别名访问命名空间或类型的结果与通过声明名称访问该命名空间或类型的结果完全相同。

使用别名可以命名封闭的构造类型,但不能在没有提供类型参数的情况下为未绑定泛型类型声明命名。

全局使用命名空间指令

global_using_namespace_directive 将命名空间中包含的类型导入到程序中,使每种类型的标识符无需限定即可使用。

global_using_namespace_directive
    : 'global' 'using' namespace_name ';'
    ;

在包含 global_using_namespace_directive的程序的成员声明中,可以直接引用给定命名空间中包含的类型。

global_using_namespace_directive 指令导入给定命名空间中包含的类型,但特别要注意的是,它不导入嵌套命名空间。

global_using_alias_directive不同,global_using_namespace_directive 可以导入已在程序的编译单元中定义的标识符的类型。 实际上,在给定的编译单元中,由程序中任何 global_using_namespace_directive 导入的名称被编译单元中类似命名的成员隐藏。

当同一程序中由 global_using_namespace_directives 或 global_using_static_directives 导入的多个命名空间或类型包含同名的类型时,该名称被作为 type_name 的引用被视为不明确。

此外,当同一程序中由 global_using_namespace_directiveglobal_using_static_directive导入的多个命名空间或类型包含同名的类型或成员时,作为 simple_name 的名称引用被视为不明确。

global_using_namespace_directive 引用的 namespace_name 的解析方式与由 global_using_alias_directive 引用的 namespace_or_type_name 的解析方式相同。 因此,同一程序中 global_using_namespace_directive 不会影响彼此,并且可以按任意顺序编写。

全局使用静态指令

global_using_static_directive 将直接包含在类型声明中的嵌套类型和静态成员导入到所涉程序中,使每个成员和类型的标识符可以不加限定地使用。

global_using_static_directive
    : 'global' 'using' 'static' type_name ';'
    ;

在包含 global_using_static_directive的程序中,成员声明内可以直接引用在给定类型声明中直接包含的可访问的嵌套类型和静态成员(但不包括扩展方法)。

global_using_static_directive 专门不直接将扩展方法导入为静态方法,而是使它们可用于扩展方法调用。

global_using_static_directive 仅导入直接在给定类型中声明的成员和类型,而不是在基类中声明的成员和类型。

在上面的 global_using_namespace_directive 部分中,讨论了多个 global_using_namespace_directiveglobal_using_static_directives 之间的歧义。

限定的别名成员 §14.8

对确定 qualified_alias_member 含义的算法进行了修改,具体如下所示。

这是相关的项目符号,附有建议添加的内容(以粗体显示):

  • 否则,从命名空间声明(§14.3)开始,紧接包含 限定别名成员(如果有),然后继续处理每个封闭的命名空间声明(如果有),以包含 限定别名成员的编译单元结尾,依次评估以下步骤,直到找到一个实体:

    • 如果命名空间声明或编译单元包含将 N 与类型、或到达编译单元关联的 using_alias_directive,则程序包含将 与类型关联的 global_using_alias_directiveN 则未定义 qualified_alias_member 并发生编译时错误。
    • 否则,如果命名空间声明或编译单元包含将 与命名空间关联的 extern_alias_directiveusing_alias_directiveN则当到达编译单元时,程序将包含将 与命名空间关联的 global_using_alias_directiveN,然后
      • 如果与 N 关联的命名空间包含名为 IK 为零的命名空间,则 qualified_alias_member 引用该命名空间。
      • 否则,如果与 N 关联的命名空间包含名为 IK 为零的非泛型类型,则 qualified_alias_member 引用该类型。
      • 否则,如果与 N 关联的命名空间包含具有 K 类型参数的名为 I 的类型,则 qualified_alias_member 引用使用给定类型参数构造的类型。
      • 否则,qualified_alias_member 未定义且发生编译时错误。