csharplang/spec/namespaces.md

607 lines
26 KiB
Markdown
Raw Permalink Normal View History

# Namespaces
C# programs are organized using namespaces. Namespaces are used both as an "internal" organization system for a program, and as an "external" organization system—a way of presenting program elements that are exposed to other programs.
Using directives ([Using directives](namespaces.md#using-directives)) are provided to facilitate the use of namespaces.
## Compilation units
A *compilation_unit* defines the overall structure of a source file. A compilation unit consists of zero or more *using_directive*s followed by zero or more *global_attributes* followed by zero or more *namespace_member_declaration*s.
```antlr
compilation_unit
: extern_alias_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 *using_directive*s of a compilation unit affect the *global_attributes* and *namespace_member_declaration*s of that compilation unit, but have no effect on other compilation units.
The *global_attributes* ([Attributes](attributes.md)) of a compilation unit permit the specification of attributes for the target assembly and module. Assemblies and modules act as physical containers for types. An assembly may consist of several physically separate modules.
The *namespace_member_declaration*s of each compilation unit of a program contribute members to a single declaration space called the global namespace. For example:
File `A.cs`:
```csharp
class A {}
```
File `B.cs`:
```csharp
class B {}
```
The two compilation units contribute to the single global namespace, in this case declaring two classes with the fully qualified names `A` and `B`. Because the two compilation units contribute to the same declaration space, it would have been an error if each contained a declaration of a member with the same name.
## Namespace declarations
A *namespace_declaration* consists of the keyword `namespace`, followed by a namespace name and body, optionally followed by a semicolon.
```antlr
namespace_declaration
: 'namespace' qualified_identifier namespace_body ';'?
;
qualified_identifier
: identifier ('.' identifier)*
;
namespace_body
: '{' extern_alias_directive* using_directive* namespace_member_declaration* '}'
;
```
A *namespace_declaration* may occur as a top-level declaration in a *compilation_unit* or as a member declaration within another *namespace_declaration*. When a *namespace_declaration* occurs as a top-level declaration in a *compilation_unit*, the namespace becomes a member of the global namespace. When a *namespace_declaration* occurs within another *namespace_declaration*, the inner namespace becomes a member of the outer namespace. In either case, the name of a namespace must be unique within the containing namespace.
Namespaces are implicitly `public` and the declaration of a namespace cannot include any access modifiers.
Within a *namespace_body*, the optional *using_directive*s import the names of other namespaces, types and members, allowing them to be referenced directly instead of through qualified names. The optional *namespace_member_declaration*s contribute members to the declaration space of the namespace. Note that all *using_directive*s must appear before any member declarations.
The *qualified_identifier* of a *namespace_declaration* may be a single identifier or a sequence of identifiers separated by "`.`" tokens. The latter form permits a program to define a nested namespace without lexically nesting several namespace declarations. For example,
```csharp
namespace N1.N2
{
class A {}
class B {}
}
```
is semantically equivalent to
```csharp
namespace N1
{
namespace N2
{
class A {}
class B {}
}
}
```
Namespaces are open-ended, and two namespace declarations with the same fully qualified name contribute to the same declaration space ([Declarations](basic-concepts.md#declarations)). In the example
```csharp
namespace N1.N2
{
class A {}
}
namespace N1.N2
{
class B {}
}
```
the two namespace declarations above contribute to the same declaration space, in this case declaring two classes with the fully qualified names `N1.N2.A` and `N1.N2.B`. Because the two declarations contribute to the same declaration space, it would have been an error if each contained a declaration of a member with the same name.
## Extern aliases
An *extern_alias_directive* introduces an identifier that serves as an alias for a namespace. The specification of the aliased namespace is external to the source code of the program and applies also to nested namespaces of the aliased namespace.
```antlr
extern_alias_directive
: 'extern' 'alias' identifier ';'
;
```
The scope of an *extern_alias_directive* extends over the *using_directive*s, *global_attributes* and *namespace_member_declaration*s of its immediately containing compilation unit or namespace body.
Within a compilation unit or namespace body that contains an *extern_alias_directive*, the identifier introduced by the *extern_alias_directive* can be used to reference the aliased namespace. It is a compile-time error for the *identifier* to be the word `global`.
An *extern_alias_directive* makes an alias available within a particular compilation unit or namespace body, but it 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 following program declares and uses two extern aliases, `X` and `Y`, each of which represent the root of a distinct namespace hierarchy:
```csharp
extern alias X;
extern alias Y;
class Test
{
X::N.A a;
X::N.B b1;
Y::N.B b2;
Y::N.C c;
}
```
The program declares the existence of the extern aliases `X` and `Y`, but the actual definitions of the aliases are external to the program. The identically named `N.B` classes can now be referenced as `X.N.B` and `Y.N.B`, or, using the namespace alias qualifier, `X::N.B` and `Y::N.B`. An error occurs if a program declares an extern alias for which no external definition is provided.
## Using directives
***Using directives*** facilitate the use of namespaces and types defined in other namespaces. Using directives impact the name resolution process of *namespace_or_type_name*s ([Namespace and type names](basic-concepts.md#namespace-and-type-names)) and *simple_name*s ([Simple names](expressions.md#simple-names)), but unlike declarations, using directives do not contribute new members to the underlying declaration spaces of the compilation units or namespaces within which they are used.
```antlr
using_directive
: using_alias_directive
| using_namespace_directive
| using_static_directive
;
```
A *using_alias_directive* ([Using alias directives](namespaces.md#using-alias-directives)) introduces an alias for a namespace or type.
A *using_namespace_directive* ([Using namespace directives](namespaces.md#using-namespace-directives)) imports the type members of a namespace.
A *using_static_directive* ([Using static directives](namespaces.md#using-static-directives)) imports the nested types and static members of a type.
The scope of a *using_directive* extends over the *namespace_member_declaration*s of its immediately containing compilation unit or namespace body. The scope of a *using_directive* specifically does not include its peer *using_directive*s. Thus, peer *using_directive*s do not affect each other, and the order in which they are written is insignificant.
### Using alias directives
A *using_alias_directive* introduces an identifier that serves as an alias for a namespace or type within the immediately enclosing compilation unit or namespace body.
```antlr
using_alias_directive
: 'using' identifier '=' namespace_or_type_name ';'
;
```
Within member declarations in a compilation unit or namespace body that contains a *using_alias_directive*, the identifier introduced by the *using_alias_directive* can be used to reference the given namespace or type. For example:
```csharp
namespace N1.N2
{
class A {}
}
namespace N3
{
using A = N1.N2.A;
class B: A {}
}
```
Above, within member declarations in the `N3` namespace, `A` is an alias for `N1.N2.A`, and thus class `N3.B` derives from class `N1.N2.A`. The same effect can be obtained by creating an alias `R` for `N1.N2` and then referencing `R.A`:
```csharp
namespace N3
{
using R = N1.N2;
class B: R.A {}
}
```
The *identifier* of a *using_alias_directive* must be unique within the declaration space of the compilation unit or namespace that immediately contains the *using_alias_directive*. For example:
```csharp
namespace N3
{
class A {}
}
namespace N3
{
using A = N1.N2.A; // Error, A already exists
}
```
Above, `N3` already contains a member `A`, so it is a compile-time error for a *using_alias_directive* to use that identifier. Likewise, it is a compile-time error for two or more *using_alias_directive*s in the same compilation unit or namespace body to declare aliases by the same name.
A *using_alias_directive* makes an alias available within a particular compilation unit or namespace body, but it does not contribute any new members to the underlying declaration space. In other words, a *using_alias_directive* is not transitive but rather affects only the compilation unit or namespace body in which it occurs. In the example
```csharp
namespace N3
{
using R = N1.N2;
}
namespace N3
{
class B: R.A {} // Error, R unknown
}
```
the scope of the *using_alias_directive* that introduces `R` only extends to member declarations in the namespace body in which it is contained, so `R` is unknown in the second namespace declaration. However, placing the *using_alias_directive* in the containing compilation unit causes the alias to become available within both namespace declarations:
```csharp
using R = N1.N2;
namespace N3
{
class B: R.A {}
}
namespace N3
{
class C: R.A {}
}
```
Just like regular members, names introduced by *using_alias_directive*s are hidden by similarly named members in nested scopes. In the example
```csharp
using R = N1.N2;
namespace N3
{
class R {}
class B: R.A {} // Error, R has no member A
}
```
the reference to `R.A` in the declaration of `B` causes a compile-time error because `R` refers to `N3.R`, not `N1.N2`.
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. 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. A *using_alias_directive* may however be affected by *extern_alias_directive*s in the immediately containing compilation unit or namespace body. In the example
```csharp
namespace N1.N2 {}
namespace N3
{
extern alias E;
using R1 = E.N; // OK
using R2 = N1; // OK
using R3 = N1.N2; // OK
using R4 = R2.N2; // Error, R2 unknown
}
```
the last *using_alias_directive* results in a compile-time error because it is not affected by the first *using_alias_directive*. The first *using_alias_directive* does not result in an error since the scope of the extern alias `E` includes the *using_alias_directive*.
A *using_alias_directive* can create an alias for any namespace or type, including the namespace within which it appears and any namespace or type nested within that namespace.
Accessing a namespace or type through an alias yields exactly the same result as accessing that namespace or type through its declared name. For example, given
```csharp
namespace N1.N2
{
class A {}
}
namespace N3
{
using R1 = N1;
using R2 = N1.N2;
class B
{
N1.N2.A a; // refers to N1.N2.A
R1.N2.A b; // refers to N1.N2.A
R2.A c; // refers to N1.N2.A
}
}
```
the names `N1.N2.A`, `R1.N2.A`, and `R2.A` are equivalent and all refer to the class whose fully qualified name is `N1.N2.A`.
Using aliases can name a closed constructed type, but cannot name an unbound generic type declaration without supplying type arguments. For example:
```csharp
namespace N1
{
class A<T>
{
class B {}
}
}
namespace N2
{
using W = N1.A; // Error, cannot name unbound generic type
using X = N1.A.B; // Error, cannot name unbound generic type
using Y = N1.A<int>; // Ok, can name closed constructed type
using Z<T> = N1.A<T>; // Error, using alias cannot have type parameters
}
```
### Using namespace directives
A *using_namespace_directive* imports the types contained in a namespace into the immediately enclosing compilation unit or namespace body, enabling the identifier of each type to be used without qualification.
```antlr
using_namespace_directive
: 'using' namespace_name ';'
;
```
Within member declarations in a compilation unit or namespace body that contains a *using_namespace_directive*, the types contained in the given namespace can be referenced directly. For example:
```csharp
namespace N1.N2
{
class A {}
}
namespace N3
{
using N1.N2;
class B: A {}
}
```
Above, within member declarations in the `N3` namespace, the type members of `N1.N2` are directly available, and thus class `N3.B` derives from class `N1.N2.A`.
A *using_namespace_directive* imports the types contained in the given namespace, but specifically does not import nested namespaces. In the example
```csharp
namespace N1.N2
{
class A {}
}
namespace N3
{
using N1;
class B: N2.A {} // Error, N2 unknown
}
```
the *using_namespace_directive* imports the types contained in `N1`, but not the namespaces nested in `N1`. Thus, the reference to `N2.A` in the declaration of `B` results in a compile-time error because no members named `N2` are in scope.
Unlike a *using_alias_directive*, a *using_namespace_directive* may import types whose identifiers are already defined within the enclosing compilation unit or namespace body. In effect, names imported by a *using_namespace_directive* are hidden by similarly named members in the enclosing compilation unit or namespace body. For example:
```csharp
namespace N1.N2
{
class A {}
class B {}
}
namespace N3
{
using N1.N2;
class A {}
}
```
Here, within member declarations in the `N3` namespace, `A` refers to `N3.A` rather than `N1.N2.A`.
When more than one namespace or type imported by *using_namespace_directive*s or *using_static_directive*s in the same compilation unit or namespace body contain types by the same name, references to that name as a *type_name* are considered ambiguous. In the example
```csharp
namespace N1
{
class A {}
}
namespace N2
{
class A {}
}
namespace N3
{
using N1;
using N2;
class B: A {} // Error, A is ambiguous
}
```
both `N1` and `N2` contain a member `A`, and because `N3` imports both, referencing `A` in `N3` is a compile-time error. In this situation, the conflict can be resolved either through qualification of references to `A`, or by introducing a *using_alias_directive* that picks a particular `A`. For example:
```csharp
namespace N3
{
using N1;
using N2;
using A = N1.A;
class B: A {} // A means N1.A
}
```
Furthermore, when more than one namespace or type imported by *using_namespace_directive*s or *using_static_directive*s in the same compilation unit or namespace body contain types or members by the same name, references to that name as a *simple_name* are considered ambiguous. In the example
```csharp
namespace N1
{
class A {}
}
class C
{
2018-07-13 11:15:51 +02:00
public static int A;
}
namespace N2
{
using N1;
using static C;
class B
{
void M()
{
A a = new A(); // Ok, A is unambiguous as a type-name
A.Equals(2); // Error, A is ambiguous as a simple-name
2018-07-13 11:15:51 +02:00
}
}
}
```
`N1` contains a type member `A`, and `C` contains a static field `A`, and because `N2` imports both, referencing `A` as a *simple_name* is ambiguous and a compile-time error.
Like a *using_alias_directive*, a *using_namespace_directive* does not contribute any new members to the underlying declaration space of the compilation unit or namespace, but rather affects only the compilation unit or namespace body in which it appears.
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_directive*s in the same compilation unit or namespace body do not affect each other and can be written in any order.
### Using static directives
A *using_static_directive* imports the nested types and static members contained directly in a type declaration into the immediately enclosing compilation unit or namespace body, enabling the identifier of each member and type to be used without qualification.
```antlr
using_static_directive
: 'using' 'static' type_name ';'
;
```
Within member declarations in a compilation unit or namespace body that contains a *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. For example:
```csharp
namespace N1
{
class A
{
public class B{}
public static B M(){ return new B(); }
}
}
namespace N2
{
using static N1.A;
class C
{
void N() { B b = M(); }
}
}
```
Above, within member declarations in the `N2` namespace, the static members and nested types of `N1.A` are directly available, and thus the method `N` is able to reference both the `B` and `M` members of `N1.A`.
A *using_static_directive* specifically does not import extension methods directly as static methods, but makes them available for extension method invocation ([Extension method invocations](expressions.md#extension-method-invocations)). In the example
```csharp
namespace N1
{
static class A
{
public static void M(this string s){}
}
}
namespace N2
{
using static N1.A;
class B
{
void N()
{
M("A"); // Error, M unknown
"B".M(); // Ok, M known as extension method
N1.A.M("C"); // Ok, fully qualified
}
}
}
```
the *using_static_directive* imports the extension method `M` contained in `N1.A`, but only as an extension method. Thus, the first reference to `M` in the body of `B.N` results in a compile-time error because no members named `M` are in scope.
A *using_static_directive* only imports members and types declared directly in the given type, not members and types declared in base classes.
TODO: Example
Ambiguities between multiple *using_namespace_directives* and *using_static_directives* are discussed in [Using namespace directives](namespaces.md#using-namespace-directives).
## Namespace members
A *namespace_member_declaration* is either a *namespace_declaration* ([Namespace declarations](namespaces.md#namespace-declarations)) or a *type_declaration* ([Type declarations](namespaces.md#type-declarations)).
```antlr
namespace_member_declaration
: namespace_declaration
| type_declaration
;
```
A compilation unit or a namespace body can contain *namespace_member_declaration*s, and such declarations contribute new members to the underlying declaration space of the containing compilation unit or namespace body.
## Type declarations
A *type_declaration* is a *class_declaration* ([Class declarations](classes.md#class-declarations)), a *struct_declaration* ([Struct declarations](structs.md#struct-declarations)), an *interface_declaration* ([Interface declarations](interfaces.md#interface-declarations)), an *enum_declaration* ([Enum declarations](enums.md#enum-declarations)), or a *delegate_declaration* ([Delegate declarations](delegates.md#delegate-declarations)).
```antlr
type_declaration
: class_declaration
| struct_declaration
| interface_declaration
| enum_declaration
| delegate_declaration
;
```
A *type_declaration* can occur as a top-level declaration in a compilation unit or as a member declaration within a namespace, class, or struct.
When a type declaration for a type `T` occurs as a top-level declaration in a compilation unit, the fully qualified name of the newly declared type is simply `T`. When a type declaration for a type `T` occurs within a namespace, class, or struct, the fully qualified name of the newly declared type is `N.T`, where `N` is the fully qualified name of the containing namespace, class, or struct.
A type declared within a class or struct is called a nested type ([Nested types](classes.md#nested-types)).
The permitted access modifiers and the default access for a type declaration depend on the context in which the declaration takes place ([Declared accessibility](basic-concepts.md#declared-accessibility)):
* Types declared in compilation units or namespaces can have `public` or `internal` access. The default is `internal` access.
* Types declared in classes can have `public`, `protected internal`, `protected`, `internal`, or `private` access. The default is `private` access.
* Types declared in structs can have `public`, `internal`, or `private` access. The default is `private` access.
## Namespace alias qualifiers
The ***namespace alias qualifier*** `::` makes it possible to guarantee that type name lookups are unaffected by the introduction of new types and members. The namespace alias qualifier always appears between two identifiers referred to as the left-hand and right-hand identifiers. Unlike the regular `.` qualifier, the left-hand identifier of the `::` qualifier is looked up only as an extern or using alias.
A *qualified_alias_member* is defined as follows:
```antlr
qualified_alias_member
: identifier '::' identifier type_argument_list?
;
```
A *qualified_alias_member* can be used as a *namespace_or_type_name* ([Namespace and type names](basic-concepts.md#namespace-and-type-names)) or as the left operand in a *member_access* ([Member access](expressions.md#member-access)).
A *qualified_alias_member* has one of two forms:
* `N::I<A1, ..., Ak>`, where `N` and `I` represent identifiers, and `<A1, ..., Ak>` is a type argument list. (`K` is always at least one.)
* `N::I`, where `N` and `I` represent identifiers. (In this case, `K` is considered to be zero.)
Using this notation, the meaning of a *qualified_alias_member* is determined as follows:
* If `N` is the identifier `global`, then the global namespace is searched for `I`:
* If the global namespace contains a namespace named `I` and `K` is zero, then the *qualified_alias_member* refers to that namespace.
* Otherwise, if the global namespace contains a non-generic type named `I` and `K` is zero, then the *qualified_alias_member* refers to that type.
* Otherwise, if the global namespace 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.
* Otherwise, the *qualified_alias_member* is undefined and a compile-time error occurs.
* Otherwise, starting with the namespace declaration ([Namespace declarations](namespaces.md#namespace-declarations)) 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, 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, then:
* If the namespace associated with `N` contains a namespace named `I` and `K` is zero, then the *qualified_alias_member* refers to that namespace.
* Otherwise, if the namespace associated with `N` contains a non-generic type named `I` and `K` is zero, then the *qualified_alias_member* refers to that type.
* Otherwise, if the namespace associated with `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.
* Otherwise, the *qualified_alias_member* is undefined and a compile-time error occurs.
* Otherwise, the *qualified_alias_member* is undefined and a compile-time error occurs.
Note that using the namespace alias qualifier with an alias that references a type causes a compile-time error. Also note that if the identifier `N` is `global`, then lookup is performed in the global namespace, even if there is a using alias associating `global` with a type or namespace.
### Uniqueness of aliases
Each compilation unit and namespace body has a separate declaration space for extern aliases and using aliases. Thus, while the name of an extern alias or using alias must be unique within the set of extern aliases and using aliases declared in the immediately containing compilation unit or namespace body, an alias is permitted to have the same name as a type or namespace as long as it is used only with the `::` qualifier.
In the example
```csharp
namespace N
{
public class A {}
public class B {}
}
namespace N
{
using A = System.IO;
class X
{
A.Stream s1; // Error, A is ambiguous
A::Stream s2; // Ok
}
}
```
the name `A` has two possible meanings in the second namespace body because both the class `A` and the using alias `A` are in scope. For this reason, use of `A` in the qualified name `A.Stream` is ambiguous and causes a compile-time error to occur. However, use of `A` with the `::` qualifier is not an error because `A` is looked up only as a namespace alias.