Global using directive - C# feature specifications (original) (raw)
Note
This article is a feature specification. The specification serves as the design document for the feature. It includes proposed specification changes, along with information needed during the design and development of the feature. These articles are published until the proposed spec changes are finalized and incorporated in the current ECMA specification.
There may be some discrepancies between the feature specification and the completed implementation. Those differences are captured in the pertinent language design meeting (LDM) notes.
You can learn more about the process for adopting feature speclets into the C# language standard in the article on the specifications.
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 _global_using_directive_s are allowed only on the Compilation Unit level (cannot be used inside a namespace_declaration).
- The _global_using_directive_s, if any, must precede any _using_directive_s.
- The scope of a _global_using_directive_s extends over the _namespace_member_declaration_s of all compilation units within the program. The scope of a global_using_directive specifically does not include other _global_using_directive_s. Thus, peer _global_using_directive_s or those from a different compilation unit do not affect each other, and the order in which they are written is insignificant. The scope of a global_using_directive specifically does not include _using_directive_s immediately contained in any compilation unit of the program.
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.
§7.7 Scopes
These are the relevant bullet points with proposed additions (which are in bold):
- The scope of name defined by an extern_alias_directive extends over the _global_using_directive_s, _using_directive_s, global_attributes and _namespace_member_declaration_s of its immediately containing compilation unit or namespace body. An extern_alias_directive does not contribute any new members to the underlying declaration space. In other words, an extern_alias_directive is not transitive, but, rather, affects only the compilation unit or namespace body in which it occurs.
- The scope of a name defined or imported by a global_using_directive extends over the global_attributes and _namespace_member_declaration_s of all the _compilation_unit_s in the program.
§7.8 Namespace and type names
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):
- If the namespace_or_type_name is of the form
I
or of the formI<A1, ..., Ak>
:- If
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 nameI
, then the namespace_or_type_name refers to that type parameter. - Otherwise, if the namespace_or_type_name appears within a type declaration, then for each instance type
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):
* IfK
is zero and the declaration ofT
includes a type parameter with nameI
, then the namespace_or_type_name refers to that type parameter.
* Otherwise, if the namespace_or_type_name appears within the body of the type declaration, andT
or any of its base types contain a nested accessible type having nameI
andK
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. - If the previous steps were unsuccessful then, for each namespace
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:
* IfK
is zero andI
is the name of a namespace inN
, then:
* If the location where the namespace_or_type_name occurs is enclosed by a namespace declaration forN
and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the nameI
with a namespace or type, or any namespace declaration forN
in the program contains a global_using_alias_directive that associates the nameI
with a namespace or type, then the namespace_or_type_name is ambiguous and a compile-time error occurs.
* Otherwise, the namespace_or_type_name refers to the namespace namedI
inN
.
* Otherwise, ifN
contains an accessible type having nameI
andK
type parameters, then:
* IfK
is zero and the location where the namespace_or_type_name occurs is enclosed by a namespace declaration forN
and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the nameI
with a namespace or type, or any namespace declaration forN
in the program contains a global_using_alias_directive that associates the nameI
with a namespace or type, then the namespace_or_type_name is ambiguous and a compile-time error occurs.
* Otherwise, the namespace_or_type_name refers to the type constructed with the given type arguments.
* Otherwise, if the location where the namespace_or_type_name occurs is enclosed by a namespace declaration forN
:
* IfK
is zero and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the nameI
with an imported namespace or type, or any namespace declaration forN
in the program contains a global_using_alias_directive that associates the nameI
with an imported namespace or type, then the namespace_or_type_name refers to that namespace or type.
* Otherwise, if the namespaces and type declarations imported by the _using_namespace_directive_s and _using_alias_directive_s of the namespace declaration and the namespaces and type declarations imported by the _global_using_namespace_directive_s and _global_using_static_directive_s of any namespace declaration forN
in the program contain exactly one accessible type having nameI
andK
type parameters, then the namespace_or_type_name refers to that type constructed with the given type arguments.
* Otherwise, if the namespaces and type declarations imported by the _using_namespace_directive_s and _using_alias_directive_s of the namespace declaration and the namespaces and type declarations imported by the _global_using_namespace_directive_s and _global_using_static_directive_s of any namespace declaration forN
in the program contain more than one accessible type having nameI
andK
type parameters, then the namespace_or_type_name is ambiguous and an error occurs. - Otherwise, the namespace_or_type_name is undefined and a compile-time error occurs.
- If
Simple names §12.8.4
Changes are made to the simple_name evaluation rules as follows.
This is the relevant bullet point with proposed additions (which are in bold):
- Otherwise, for each namespace
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:- If
K
is zero andI
is the name of a namespace inN
, then:
* If the location where the simple_name occurs is enclosed by a namespace declaration forN
and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the nameI
with a namespace or type, or any namespace declaration forN
in the program contains a global_using_alias_directive that associates the nameI
with a namespace or type, then the simple_name is ambiguous and a compile-time error occurs.
* Otherwise, the simple_name refers to the namespace namedI
inN
. - Otherwise, if
N
contains an accessible type having nameI
andK
type parameters, then:
* IfK
is zero and the location where the simple_name occurs is enclosed by a namespace declaration forN
and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the nameI
with a namespace or type, or any namespace declaration forN
in the program contains a global_using_alias_directive that associates the nameI
with a namespace or type, then the simple_name is ambiguous and a compile-time error occurs.
* Otherwise, the namespace_or_type_name refers to the type constructed with the given type arguments. - Otherwise, if the location where the simple_name occurs is enclosed by a namespace declaration for
N
:
* IfK
is zero and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the nameI
with an imported namespace or type, or any namespace declaration forN
in the program contains a global_using_alias_directive that associates the nameI
with an imported namespace or type, then the simple_name refers to that namespace or type.
* Otherwise, if the namespaces and type declarations imported by the _using_namespace_directive_s and _using_static_directive_s of the namespace declaration and the namespaces and type declarations imported by the _global_using_namespace_directive_s and _global_using_static_directive_s of any namespace declaration forN
in the program contain exactly one accessible type or non-extension static member having nameI
andK
type parameters, then the simple_name refers to that type or member constructed with the given type arguments.
* Otherwise, if the namespaces and types imported by the _using_namespace_directive_s of the namespace declaration and the namespaces and type declarations imported by the _global_using_namespace_directive_s and _global_using_static_directive_s of any namespace declaration forN
in the program contain more than one accessible type or non-extension-method static member having nameI
andK
type parameters, then the simple_name is ambiguous and an error occurs.
- If
Extension method invocations §12.8.10.3
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):
- Starting with the closest enclosing namespace declaration, continuing with each enclosing namespace declaration, and ending with the containing compilation unit, successive attempts are made to find a candidate set of extension methods:
- If the given namespace or compilation unit directly contains non-generic type declarations
Ci
with eligible extension methodsMj
, then the set of those extension methods is the candidate set. - If types
Ci
imported by using_static_declarations and directly declared in namespaces imported by _using_namespace_directive_s 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_directive_s in the program directly contain eligible extension methodsMj
, then the set of those extension methods is the candidate set.
- If the given namespace or compilation unit directly contains non-generic type declarations
Compilation units §14.2
A compilation_unit defines the overall structure of a source file. A compilation unit consists of zero or more _global_using_directive_s followed by zero or more _using_directive_s followed by zero or more global_attributes followed by zero or more _namespace_member_declaration_s.
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_directive_s of a compilation unit affect the global_attributes and _namespace_member_declaration_s of all compilation units in the program.
Extern aliases §14.4
The scope of an extern_alias_directive extends over the _global_using_directive_s, _using_directive_s, global_attributes and _namespace_member_declaration_s of its immediately containing compilation unit or namespace body.
Using alias directives §14.5.2
The order in which _using_alias_directive_s 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_directive_s 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_directive_s 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_directive_s and, if the using_alias_directive is immediately contained in a compilation unit, the program had no _global_using_directive_s. A using_alias_directive may however be affected by _extern_alias_directive_s in the immediately containing compilation unit or namespace body.
Global Using alias directives
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_directive_s are hidden by similarly named members in nested scopes.
The order in which _global_using_alias_directive_s 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_directive_s or _using_directive_s 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_directive_s and the entire containing program had no _global_using_directive_s. A global_using_alias_directive may however be affected by _extern_alias_directive_s 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.
Global Using namespace directives
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_directive_s or _global_using_static_directive_s 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_directive_s or _global_using_static_directive_s 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_directive_s in the same program do not affect each other and can be written in any order.
Global Using static directives
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_directive_s and global_using_static_directives are discussed in the section for _global_using_namespace_directive_s (above).
Qualified alias member §14.8
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:
- If the namespace declaration or compilation unit contains a using_alias_directive that associates
N
with a type, or, when a compilation unit is reached, the program contains a global_using_alias_directive that associatesN
with a type, then the qualified_alias_member is undefined and a compile-time error occurs. - Otherwise, if the namespace declaration or compilation unit contains an extern_alias_directive or using_alias_directive that associates
N
with a namespace, * or, when a compilation unit is reached, the program contains a global_using_alias_directive that associatesN
with a namespace, then:
* If the namespace associated withN
contains a namespace namedI
andK
is zero, then the qualified_alias_member refers to that namespace.
* Otherwise, if the namespace associated withN
contains a non-generic type namedI
andK
is zero, then the qualified_alias_member refers to that type.
* Otherwise, if the namespace associated withN
contains a type namedI
that hasK
type parameters, then the qualified_alias_member refers to that type constructed with the given type arguments.
* Otherwise, the qualified_alias_member is undefined and a compile-time error occurs.
- If the namespace declaration or compilation unit contains a using_alias_directive that associates