ECMA-334: 10.3 Declarations
Microsoft .NET Framework, ASP.NET, Visual C# (CSharp, C Sharp, C-Sharp) Developer Training, Visual Studio
| C# Language Specification |
| © 2006 ECMA International |
10.3 Declarations
Declarations in a C# program define the constituent elements of the program. C# programs are organized using namespace declarations (§16), which can contain type declarations and nested namespace declarations. Type declarations (§16.6) are used to define classes (§17), structs (§18), interfaces (§20), enums (§21), and delegates (§22). The kinds of members permitted in a type declaration depend on the form of the type declaration. For instance, class declarations can contain declarations for constants (§17.3), fields (§17.4), methods (§17.5), properties (§17.6), events (§17.7), indexers (§17.8), operators (§17.9), instance constructors (§17.10), finalizers (§17.12), static constructors (§17.11), and nested types.
A declaration defines a name in the declaration space to which the declaration belongs. It is a compile-time error to have two or more declarations that introduce members with the same name in a declaration space, except in the following cases:
- Two or more namespace declarations with the same name are allowed in the same declaration space. Such namespace declarations are aggregated to form a single logical namespace and share a single declaration space.
- Declarations in separate programs but in the same namespace declaration space are allowed to share the same name.
- Two or more methods with the same name but distinct signatures are allowed in the same declaration space (§10.6).
- Two or more type declarations with the same name but distinct numbers of type parameters are allowed in the same declaration space (§10.8.2).
- Two or more type declarations with the
partialmodifier in the same declaration space may share the same name, same number of type parameters and same classification (class,structorinterface). In this case, the type declarations contribute to a single type and are themselves aggregated to form a single declaration space (§17.1.4).
- A namespace declaration and a type declaration in the same declaration space can share the same name as long as the type declaration has at least one type parameter (§10.8.2).
It is never possible for a type declaration space to contain different kinds of members with the same name. [Example: A type declaration space can never contain a field and a method by the same name. end example]
There are several different types of declaration spaces, as described in the following.
- Within all source files of a program, namespace-member-declarations with no enclosing namespacedeclaration are members of a single combined declaration space called the global declaration space.
- Within all source files of a program, namespace-member-declarations within namespace-declarations that have the same fully qualified namespace name are members of a single combined declaration space.
- Each compilation-unit and namespace-body has an alias declaration space. Each extern-alias-directive and using-alias-directive of the compilation-unit or namespace-body contributes a member to the alias declaration space (§16.4.1).
- Each non-partial class, struct, or interface declaration creates a new declaration space. Each partial class, struct, or interface declaration contributes to a declaration space shared by all matching parts in the same program (§17.1.4). Names are introduced into this declaration space through the type-parameter-list and class-member-declarations, struct-member-declarations, or interface-member-declarations. Except for overloaded instance constructor declarations and static constructor declarations, a class or struct member declaration cannot introduce a member by the same name as the class or struct. A class, struct, or interface permits the declaration of overloaded methods and indexers. Furthermore, a class or struct permits the declaration of overloaded instance constructors, operators, and types. [Example: A class, struct, or interface can contain multiple method declarations with the same name, provided these method declarations differ in their signature (§10.6). A class or struct can contain multiple nested types with the same name provided the types differ in the number of type parameters. end example] Base classes do not contribute to the declaration space of a class, and base interfaces do not contribute to the declaration space of an interface. Thus, a derived class or interface is allowed to declare a member with the same name as an inherited member.
- Each enumeration declaration creates a new declaration space. Names are introduced into this declaration space through enum-member-declarations.
- Each block, switch-block, for-statement, foreach-statement, or using-statement creates a declaration space for local variables and local constants called the local variable declaration space. Names are introduced into this declaration space through local-variable-declarations and local-constant-declarations. If a block is the body of an instance constructor, method, or operator declaration, or a get or set accessor for an indexer declaration, the parameters declared in such a declaration are members of the block’s local variable declaration space. If a block is the body of a generic method, the type parameters declared in such a declaration are members of the block’s local variable declaration space. It is an error for two members of a local variable declaration space to have the same name. It is an error for a local variable declaration space and a nested local variable declaration space to contain elements with the same name. [Note: Thus, within a nested block it is not possible to declare a local variable or constant with the same name as a local variable or constant in an enclosing block. It is possible for two nested blocks to contain elements with the same name as long as neither block contains the other. end note]
- Each block or switch-block creates a separate declaration space for labels called the label declaration space of the block. Names are introduced into this declaration space through labeled-statements, and the names are referenced through goto-statements. It is an error for the label declaration space of a block and the label declaration space of a nested block to contain elements with the same name. Thus, within a nested block it is not possible to declare a label with the same name as a label in an enclosing block. [Note: It is possible for two nested blocks to contain elements with the same name as long as neither block contains the other. end note]
The textual order in which names are declared is generally of no significance. In particular, textual order is not significant for the declaration and use of namespaces, constants, methods, properties, events, indexers, operators, instance constructors, finalizers, static constructors, and types. Declaration order is significant in the following ways:
- Declaration order for field declarations and local variable declarations determines the order in which their initializers (if any) are executed. When there are field declarations in multiple partial type declarations for the same type, the order of the parts is unspecified. However, within each part the field
initializers are executed in order.
- Local variables and local constants shall be defined before they are used (§10.7).
- Declaration order for enum member declarations (§21.3) is significant when constant-expression values are omitted.
[Example: The declaration space of a namespace is “open ended”, and two namespace declarations with the same fully qualified name contribute to the same declaration space. For example
namespace Megacorp.Data { class Customer { … } } namespace Megacorp.Data { class Order { … } }
The two namespace declarations above contribute to the same declaration space, in this case declaring two
classes with the fully qualified names Megacorp.Data.Customer and Megacorp.Data.Order. Because
the two declarations contribute to the same declaration space, it would have caused a compile-time error if
each contained a declaration of a class with the same name.
All declarations of a partial type contribute to the same declaration space:
partial class C { int F; partial struct N1 {…} partial class N2 {…} partial class N3 {…} } partial class C { void F() {…} // Error: conflicts with field F partial class N1 {…} // Error: conflicts with struct N1 class N2 {…} // Error: conflicts with other N2 partial class N3 {…} // Ok }
The two partial declarations for C combine to form a single declaration space and a single type. The field
named F in the first part conflicts with the method named F in the second part. The struct named N1 in the
first part conflicts with the class named N1 in the second part. The non-partial class N2 in the second part
conflicts with the partial class N2 in the first part. The two partial declarations for N3 combine to form a
single type C.N3. end example]
[Note: As specified above, the declaration space of a block cannot share names with the declaration spaces
of any nested blocks. Thus, in the following example, the F and G methods result in a compile-time error
because the name i is declared in the outer block and cannot be redeclared in the inner block. However, the
H and I methods are valid since the two i’s are declared in separate non-nested blocks.
class A { void F() { int i = 0; if (true) { int i = 1; } } void G() { if (true) { int i = 0; } int i = 1; } void H() { if (true) { int i = 0; } if (true) { int i = 1; } } void I() { for (int i = 0; i < 10; i++) H(); for (int i = 0; i < 10; i++) H(); } }
end note]