proposals/csharp-10.0/GlobalUsingDirective.md
[!INCLUDESpecletdisclaimer]
Champion issue: https://github.com/dotnet/csharplang/issues/3428
Syntax for a using directive is extended with an optional global keyword that can precede the using keyword:
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 ';'
;
The effect of adding a global_using_directive to a program can be thought of as the effect of adding a similar using_directive that resolves to the same target namespace or type to every compilation unit of the program. However, the target of a global_using_directive is resolved in context of the compilation unit that contains it.
These are the relevant bullet points with proposed additions (which are in bold):
Changes are made to the algorithm determining the meaning of a namespace_or_type_name as follows.
This is the relevant bullet point with proposed additions (which are in bold):
I or of the form I<A1, ..., Ak>:
K is zero and the namespace_or_type_name appears within a generic method declaration (§15.6) and if that declaration includes a type parameter (§15.2.3) with name I, then the namespace_or_type_name refers to that type parameter.T (§15.3.2), starting with the instance type of that type declaration and continuing with the instance type of each enclosing class or struct declaration (if any):
K is zero and the declaration of T includes a type parameter with name I, then the namespace_or_type_name refers to that type parameter.T or any of its base types contain a nested accessible type having name I and K type parameters, then the namespace_or_type_name refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. Note that non-type members (constants, fields, methods, properties, indexers, operators, instance constructors, destructors, and static constructors) and type members with a different number of type parameters are ignored when determining the meaning of the namespace_or_type_name.N, starting with the namespace in which the namespace_or_type_name occurs, continuing with each enclosing namespace (if any), and ending with the global namespace, the following steps are evaluated until an entity is located:
K is zero and I is the name of a namespace in N, then:
N and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the name I with a namespace or type, or any namespace declaration for N in the program contains a global_using_alias_directive that associates the name I with a namespace or type, then the namespace_or_type_name is ambiguous and a compile-time error occurs.I in N.N contains an accessible type having name I and K type parameters, then:
K is zero and the location where the namespace_or_type_name occurs is enclosed by a namespace declaration for N and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the name I with a namespace or type, or any namespace declaration for N in the program contains a global_using_alias_directive that associates the name I with a namespace or type, then the namespace_or_type_name is ambiguous and a compile-time error occurs.N:
K is zero and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the name I with an imported namespace or type, or any namespace declaration for N in the program contains a global_using_alias_directive that associates the name I with an imported namespace or type, then the namespace_or_type_name refers to that namespace or type.N in the program contain exactly one accessible type having name I and K type parameters, then the namespace_or_type_name refers to that type constructed with the given type arguments.N in the program contain more than one accessible type having name I and K type parameters, then the namespace_or_type_name is ambiguous and an error occurs.Changes are made to the simple_name evaluation rules as follows.
This is the relevant bullet point with proposed additions (which are in bold):
N, starting with the namespace in which the simple_name occurs, continuing with each enclosing namespace (if any), and ending with the global namespace, the following steps are evaluated until an entity is located:
K is zero and I is the name of a namespace in N, then:
N and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the name I with a namespace or type, or any namespace declaration for N in the program contains a global_using_alias_directive that associates the name I with a namespace or type, then the simple_name is ambiguous and a compile-time error occurs.I in N.N contains an accessible type having name I and K type parameters, then:
K is zero and the location where the simple_name occurs is enclosed by a namespace declaration for N and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the name I with a namespace or type, or any namespace declaration for N in the program contains a global_using_alias_directive that associates the name I with a namespace or type, then the simple_name is ambiguous and a compile-time error occurs.N:
K is zero and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the name I with an imported namespace or type, or any namespace declaration for N in the program contains a global_using_alias_directive that associates the name I with an imported namespace or type, then the simple_name refers to that namespace or type.N in the program contain exactly one accessible type or non-extension static member having name I and K type parameters, then the simple_name refers to that type or member constructed with the given type arguments.N in the program contain more than one accessible type or non-extension-method static member having name I and K type parameters, then the simple_name is ambiguous and an error occurs.Changes are made to the algorithm to find the best type_name C as follows.
This is the relevant bullet point with proposed additions (which are in bold):
Ci with eligible extension methods Mj, then the set of those extension methods is the candidate set.Ci imported by using_static_declarations and directly declared in namespaces imported by using_namespace_directives in the given namespace or compilation unit and, if containing compilation unit is reached, imported by global_using_static_declarations and directly declared in namespaces imported by global_using_namespace_directives in the program directly contain eligible extension methods Mj, then the set of those extension methods is the candidate set.A compilation_unit defines the overall structure of a source file. A compilation unit consists of zero or more global_using_directives followed by zero or more using_directives followed by zero or more global_attributes followed by zero or more namespace_member_declarations.
compilation_unit
: extern_alias_directive* global_using_directive* using_directive* global_attributes? namespace_member_declaration*
;
A C# program consists of one or more compilation units, each contained in a separate source file. When a C# program is compiled, all of the compilation units are processed together. Thus, compilation units can depend on each other, possibly in a circular fashion.
The global_using_directives of a compilation unit affect the global_attributes and namespace_member_declarations of all compilation units in the program.
The scope of an extern_alias_directive extends over the global_using_directives, using_directives, global_attributes and namespace_member_declarations of its immediately containing compilation unit or namespace body.
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, and, if the using_alias_directive is immediately contained in a compilation unit, is not affected by the global_using_directives in the program. 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 and, if the using_alias_directive is immediately contained in a compilation unit, the program had no global_using_directives. A using_alias_directive may however be affected by extern_alias_directives in the immediately containing compilation unit or namespace body.
A global_using_alias_directive introduces an identifier that serves as an alias for a namespace or type within the program.
global_using_alias_directive
: 'global' 'using' identifier '=' namespace_or_type_name ';'
;
Within member declarations in any compilation unit of a program that contains a global_using_alias_directive, the identifier introduced by the global_using_alias_directive can be used to reference the given namespace or type.
The identifier of a global_using_alias_directive must be unique within the declaration space of any compilation unit of a program that contains the global_using_alias_directive.
Just like regular members, names introduced by global_using_alias_directives are hidden by similarly named members in nested scopes.
The order in which global_using_alias_directives are written has no significance, and resolution of the namespace_or_type_name referenced by a global_using_alias_directive is not affected by the global_using_alias_directive itself or by other global_using_directives or using_directives in the program. In other words, the namespace_or_type_name of a global_using_alias_directive is resolved as if the immediately containing compilation unit had no using_directives and the entire containing program had no global_using_directives. A global_using_alias_directive may however be affected by extern_alias_directives in the immediately containing compilation unit.
A global_using_alias_directive can create an alias for any namespace or type.
Accessing a namespace or type through an alias yields exactly the same result as accessing that namespace or type through its declared name.
Using aliases can name a closed constructed type, but cannot name an unbound generic type declaration without supplying type arguments.
A global_using_namespace_directive imports the types contained in a namespace into the program, enabling the identifier of each type to be used without qualification.
global_using_namespace_directive
: 'global' 'using' namespace_name ';'
;
Within member declarations in a program that contains a global_using_namespace_directive, the types contained in the given namespace can be referenced directly.
A global_using_namespace_directive imports the types contained in the given namespace, but specifically does not import nested namespaces.
Unlike a global_using_alias_directive, a global_using_namespace_directive may import types whose identifiers are already defined within a compilation unit of the program. In effect, in a given compilation unit, names imported by any global_using_namespace_directive in the program are hidden by similarly named members in the compilation unit.
When more than one namespace or type imported by global_using_namespace_directives or global_using_static_directives in the same program contain types by the same name, references to that name as a type_name are considered ambiguous.
Furthermore, when more than one namespace or type imported by global_using_namespace_directives or global_using_static_directives in the same program contain types or members by the same name, references to that name as a simple_name are considered ambiguous.
The namespace_name referenced by a global_using_namespace_directive is resolved in the same way as the namespace_or_type_name referenced by a global_using_alias_directive. Thus, global_using_namespace_directives in the same program do not affect each other and can be written in any order.
A global_using_static_directive imports the nested types and static members contained directly in a type declaration into the containing program, enabling the identifier of each member and type to be used without qualification.
global_using_static_directive
: 'global' 'using' 'static' type_name ';'
;
Within member declarations in a program that contains a global_using_static_directive, the accessible nested types and static members (except extension methods) contained directly in the declaration of the given type can be referenced directly.
A global_using_static_directive specifically does not import extension methods directly as static methods, but makes them available for extension method invocation.
A global_using_static_directive only imports members and types declared directly in the given type, not members and types declared in base classes.
Ambiguities between multiple global_using_namespace_directives and global_using_static_directives are discussed in the section for global_using_namespace_directives (above).
Changes are made to the algorithm determining the meaning of a qualified_alias_member as follows.
This is the relevant bullet point with proposed additions (which are in bold):
Otherwise, starting with the namespace declaration (§14.3) immediately containing the qualified_alias_member (if any), continuing with each enclosing namespace declaration (if any), and ending with the compilation unit containing the qualified_alias_member, the following steps are evaluated until an entity is located:
N with a type, or, when a compilation unit is reached, the program contains a global_using_alias_directive that associates N with a type, then the qualified_alias_member is undefined and a compile-time error occurs.N with a namespace, *or, when a compilation unit is reached, the program contains a global_using_alias_directive that associates N with a namespace, then:
N contains a namespace named I and K is zero, then the qualified_alias_member refers to that namespace.N contains a non-generic type named I and K is zero, then the qualified_alias_member refers to that type.N contains a type named I that has K type parameters, then the qualified_alias_member refers to that type constructed with the given type arguments.