- •Foreword
- •Introduction
- •Scope
- •Conformance
- •Normative references
- •Definitions
- •Notational conventions
- •Acronyms and abbreviations
- •General description
- •Language overview
- •Getting started
- •Types
- •Predefined types
- •Conversions
- •Array types
- •Type system unification
- •Variables and parameters
- •Automatic memory management
- •Expressions
- •Statements
- •Classes
- •Constants
- •Fields
- •Methods
- •Properties
- •Events
- •Operators
- •Indexers
- •Instance constructors
- •Destructors
- •Static constructors
- •Inheritance
- •Static classes
- •Partial type declarations
- •Structs
- •Interfaces
- •Delegates
- •Enums
- •Namespaces and assemblies
- •Versioning
- •Extern Aliases
- •Attributes
- •Generics
- •Why generics?
- •Creating and consuming generics
- •Multiple type parameters
- •Constraints
- •Generic methods
- •Anonymous methods
- •Iterators
- •Lexical structure
- •Programs
- •Grammars
- •Lexical grammar
- •Syntactic grammar
- •Grammar ambiguities
- •Lexical analysis
- •Line terminators
- •Comments
- •White space
- •Tokens
- •Unicode escape sequences
- •Identifiers
- •Keywords
- •Literals
- •Boolean literals
- •Integer literals
- •Real literals
- •Character literals
- •String literals
- •The null literal
- •Operators and punctuators
- •Pre-processing directives
- •Conditional compilation symbols
- •Pre-processing expressions
- •Declaration directives
- •Conditional compilation directives
- •Diagnostic directives
- •Region control
- •Line directives
- •Pragma directives
- •Basic concepts
- •Application startup
- •Application termination
- •Declarations
- •Members
- •Namespace members
- •Struct members
- •Enumeration members
- •Class members
- •Interface members
- •Array members
- •Delegate members
- •Member access
- •Declared accessibility
- •Accessibility domains
- •Protected access for instance members
- •Accessibility constraints
- •Signatures and overloading
- •Scopes
- •Name hiding
- •Hiding through nesting
- •Hiding through inheritance
- •Namespace and type names
- •Unqualified name
- •Fully qualified names
- •Automatic memory management
- •Execution order
- •Types
- •Value types
- •The System.ValueType type
- •Default constructors
- •Struct types
- •Simple types
- •Integral types
- •Floating point types
- •The decimal type
- •The bool type
- •Enumeration types
- •Reference types
- •Class types
- •The object type
- •The string type
- •Interface types
- •Array types
- •Delegate types
- •Boxing and unboxing
- •Boxing conversions
- •Unboxing conversions
- •Variables
- •Variable categories
- •Static variables
- •Instance variables
- •Instance variables in classes
- •Instance variables in structs
- •Array elements
- •Value parameters
- •Reference parameters
- •Output parameters
- •Local variables
- •Default values
- •Definite assignment
- •Initially assigned variables
- •Initially unassigned variables
- •Precise rules for determining definite assignment
- •General rules for statements
- •Block statements, checked, and unchecked statements
- •Expression statements
- •Declaration statements
- •If statements
- •Switch statements
- •While statements
- •Do statements
- •For statements
- •Break, continue, and goto statements
- •Throw statements
- •Return statements
- •Try-catch statements
- •Try-finally statements
- •Try-catch-finally statements
- •Foreach statements
- •Using statements
- •Lock statements
- •General rules for simple expressions
- •General rules for expressions with embedded expressions
- •Invocation expressions and object creation expressions
- •Simple assignment expressions
- •&& expressions
- •|| expressions
- •! expressions
- •?: expressions
- •Anonymous method expressions
- •Yield statements
- •Variable references
- •Atomicity of variable references
- •Conversions
- •Implicit conversions
- •Identity conversion
- •Implicit numeric conversions
- •Implicit enumeration conversions
- •Implicit reference conversions
- •Boxing conversions
- •Implicit type parameter conversions
- •Implicit constant expression conversions
- •User-defined implicit conversions
- •Explicit conversions
- •Explicit numeric conversions
- •Explicit enumeration conversions
- •Explicit reference conversions
- •Unboxing conversions
- •User-defined explicit conversions
- •Standard conversions
- •Standard implicit conversions
- •Standard explicit conversions
- •User-defined conversions
- •Permitted user-defined conversions
- •Evaluation of user-defined conversions
- •User-defined implicit conversions
- •User-defined explicit conversions
- •Anonymous method conversions
- •Method group conversions
- •Expressions
- •Expression classifications
- •Values of expressions
- •Operators
- •Operator precedence and associativity
- •Operator overloading
- •Unary operator overload resolution
- •Binary operator overload resolution
- •Candidate user-defined operators
- •Numeric promotions
- •Unary numeric promotions
- •Binary numeric promotions
- •Member lookup
- •Base types
- •Function members
- •Argument lists
- •Overload resolution
- •Applicable function member
- •Better function member
- •Better conversion
- •Function member invocation
- •Invocations on boxed instances
- •Primary expressions
- •Literals
- •Simple names
- •Invariant meaning in blocks
- •Parenthesized expressions
- •Member access
- •Identical simple names and type names
- •Invocation expressions
- •Method invocations
- •Delegate invocations
- •Element access
- •Array access
- •Indexer access
- •This access
- •Base access
- •Postfix increment and decrement operators
- •The new operator
- •Object creation expressions
- •Array creation expressions
- •Delegate creation expressions
- •The typeof operator
- •The checked and unchecked operators
- •Default value expression
- •Anonymous methods
- •Anonymous method signatures
- •Anonymous method blocks
- •Outer variables
- •Captured outer variables
- •Instantiation of local variables
- •Anonymous method evaluation
- •Implementation example
- •Unary expressions
- •Unary plus operator
- •Unary minus operator
- •Logical negation operator
- •Bitwise complement operator
- •Prefix increment and decrement operators
- •Cast expressions
- •Arithmetic operators
- •Multiplication operator
- •Division operator
- •Remainder operator
- •Addition operator
- •Subtraction operator
- •Shift operators
- •Relational and type-testing operators
- •Integer comparison operators
- •Floating-point comparison operators
- •Decimal comparison operators
- •Boolean equality operators
- •Enumeration comparison operators
- •Reference type equality operators
- •String equality operators
- •Delegate equality operators
- •The is operator
- •The as operator
- •Logical operators
- •Integer logical operators
- •Enumeration logical operators
- •Boolean logical operators
- •Conditional logical operators
- •Boolean conditional logical operators
- •User-defined conditional logical operators
- •Conditional operator
- •Assignment operators
- •Simple assignment
- •Compound assignment
- •Event assignment
- •Expression
- •Constant expressions
- •Boolean expressions
- •Statements
- •End points and reachability
- •Blocks
- •Statement lists
- •The empty statement
- •Labeled statements
- •Declaration statements
- •Local variable declarations
- •Local constant declarations
- •Expression statements
- •Selection statements
- •The if statement
- •The switch statement
- •Iteration statements
- •The while statement
- •The do statement
- •The for statement
- •The foreach statement
- •Jump statements
- •The break statement
- •The continue statement
- •The goto statement
- •The return statement
- •The throw statement
- •The try statement
- •The checked and unchecked statements
- •The lock statement
- •The using statement
- •The yield statement
- •Namespaces
- •Compilation units
- •Namespace declarations
- •Extern alias directives
- •Using directives
- •Using alias directives
- •Using namespace directives
- •Namespace members
- •Type declarations
- •Qualified alias member
- •Classes
- •Class declarations
- •Class modifiers
- •Abstract classes
- •Sealed classes
- •Static classes
- •Class base specification
- •Base classes
- •Interface implementations
- •Class body
- •Partial declarations
- •Class members
- •Inheritance
- •The new modifier
- •Access modifiers
- •Constituent types
- •Static and instance members
- •Nested types
- •Fully qualified name
- •Declared accessibility
- •Hiding
- •this access
- •Reserved member names
- •Member names reserved for properties
- •Member names reserved for events
- •Member names reserved for indexers
- •Member names reserved for destructors
- •Constants
- •Fields
- •Static and instance fields
- •Readonly fields
- •Using static readonly fields for constants
- •Versioning of constants and static readonly fields
- •Volatile fields
- •Field initialization
- •Variable initializers
- •Static field initialization
- •Instance field initialization
- •Methods
- •Method parameters
- •Value parameters
- •Reference parameters
- •Output parameters
- •Parameter arrays
- •Static and instance methods
- •Virtual methods
- •Override methods
- •Sealed methods
- •Abstract methods
- •External methods
- •Method body
- •Method overloading
- •Properties
- •Static and instance properties
- •Accessors
- •Virtual, sealed, override, and abstract accessors
- •Events
- •Field-like events
- •Event accessors
- •Static and instance events
- •Virtual, sealed, override, and abstract accessors
- •Indexers
- •Indexer overloading
- •Operators
- •Unary operators
- •Binary operators
- •Conversion operators
- •Instance constructors
- •Constructor initializers
- •Instance variable initializers
- •Constructor execution
- •Default constructors
- •Private constructors
- •Optional instance constructor parameters
- •Static constructors
- •Destructors
- •Structs
- •Struct declarations
- •Struct modifiers
- •Struct interfaces
- •Struct body
- •Struct members
- •Class and struct differences
- •Value semantics
- •Inheritance
- •Assignment
- •Default values
- •Boxing and unboxing
- •Meaning of this
- •Field initializers
- •Constructors
- •Destructors
- •Static constructors
- •Struct examples
- •Database integer type
- •Database boolean type
- •Arrays
- •Array types
- •The System.Array type
- •Array creation
- •Array element access
- •Array members
- •Array covariance
- •Arrays and the generic IList interface
- •Array initializers
- •Interfaces
- •Interface declarations
- •Interface modifiers
- •Base interfaces
- •Interface body
- •Interface members
- •Interface methods
- •Interface properties
- •Interface events
- •Interface indexers
- •Interface member access
- •Fully qualified interface member names
- •Interface implementations
- •Explicit interface member implementations
- •Interface mapping
- •Interface implementation inheritance
- •Interface re-implementation
- •Abstract classes and interfaces
- •Enums
- •Enum declarations
- •Enum modifiers
- •Enum members
- •The System.Enum type
- •Enum values and operations
- •Delegates
- •Delegate declarations
- •Delegate instantiation
- •Delegate invocation
- •Exceptions
- •Causes of exceptions
- •The System.Exception class
- •How exceptions are handled
- •Common Exception Classes
- •Attributes
- •Attribute classes
- •Attribute usage
- •Positional and named parameters
- •Attribute parameter types
- •Attribute specification
- •Attribute instances
- •Compilation of an attribute
- •Run-time retrieval of an attribute instance
- •Reserved attributes
- •The AttributeUsage attribute
- •The Conditional attribute
- •Conditional Methods
- •Conditional Attribute Classes
- •The Obsolete attribute
- •Unsafe code
- •Unsafe contexts
- •Pointer types
- •Fixed and moveable variables
- •Pointer conversions
- •Pointers in expressions
- •Pointer indirection
- •Pointer member access
- •Pointer element access
- •The address-of operator
- •Pointer increment and decrement
- •Pointer arithmetic
- •Pointer comparison
- •The sizeof operator
- •The fixed statement
- •Stack allocation
- •Dynamic memory allocation
- •Generics
- •Generic class declarations
- •Type parameters
- •The instance type
- •Members of generic classes
- •Static fields in generic classes
- •Static constructors in generic classes
- •Accessing protected members
- •Overloading in generic classes
- •Parameter array methods and type parameters
- •Overriding and generic classes
- •Operators in generic classes
- •Nested types in generic classes
- •Generic struct declarations
- •Generic interface declarations
- •Uniqueness of implemented interfaces
- •Explicit interface member implementations
- •Generic delegate declarations
- •Constructed types
- •Type arguments
- •Open and closed types
- •Base classes and interfaces of a constructed type
- •Members of a constructed type
- •Accessibility of a constructed type
- •Conversions
- •Using alias directives
- •Generic methods
- •Generic method signatures
- •Virtual generic methods
- •Calling generic methods
- •Inference of type arguments
- •Using a generic method with a delegate
- •Constraints
- •Satisfying constraints
- •Member lookup on type parameters
- •Type parameters and boxing
- •Conversions involving type parameters
- •Iterators
- •Iterator blocks
- •Enumerator interfaces
- •Enumerable interfaces
- •Yield type
- •This access
- •Enumerator objects
- •The MoveNext method
- •The Current property
- •The Dispose method
- •Enumerable objects
- •The GetEnumerator method
- •Implementation example
- •Lexical grammar
- •Line terminators
- •White space
- •Comments
- •Unicode character escape sequences
- •Identifiers
- •Keywords
- •Literals
- •Operators and punctuators
- •Pre-processing directives
- •Syntactic grammar
- •Basic concepts
- •Types
- •Expressions
- •Statements
- •Classes
- •Structs
- •Arrays
- •Interfaces
- •Enums
- •Delegates
- •Attributes
- •Generics
- •Grammar extensions for unsafe code
- •Undefined behavior
- •Implementation-defined behavior
- •Unspecified behavior
- •Other Issues
- •Capitalization styles
- •Pascal casing
- •Camel casing
- •All uppercase
- •Capitalization summary
- •Word choice
- •Namespaces
- •Classes
- •Interfaces
- •Enums
- •Static fields
- •Parameters
- •Methods
- •Properties
- •Events
- •Case sensitivity
- •Avoiding type name confusion
- •Documentation Comments
- •Introduction
- •Recommended tags
- •<code>
- •<example>
- •<exception>
- •<list>
- •<para>
- •<param>
- •<paramref>
- •<permission>
- •<remarks>
- •<returns>
- •<seealso>
- •<summary>
- •<value>
- •Processing the documentation file
- •ID string format
- •ID string examples
- •An example
- •C# source code
- •Resulting XML
C# LANGUAGE SPECIFICATION
18.13 Versioning
2Versioning is the process of evolving a component over time in a compatible manner. A new version of a
3component is source compatible with a previous version if code that depends on the previous version can,
4when recompiled, work with the new version. In contrast, a new version of a component is binary
5compatible if an application that depended on the old version can, without recompilation, work with the new
6version.
7Most languages do not support binary compatibility at all, and many do little to facilitate source
8compatibility. In fact, some languages contain flaws that make it impossible, in general, to evolve a class
9over time without breaking at least some client code.
10As an example, consider the situation of a base class author who ships a class named Base. In the first
11version, Base contains no method F. A component named Derived derives from Base, and introduces
12an F. This Derived class, along with the class Base on which it depends, is released to customers, who
13deploy to numerous clients and servers.
14// Author A
15namespace A
16{
17 |
public class Base |
// version 1 |
18 |
{ |
|
19}
20}
21// Author B
22namespace B
23{
24 |
class Derived: A.Base |
25 |
{ |
26 |
public virtual void F() { |
27 |
System.Console.WriteLine("Derived.F"); |
28 |
} |
29}
30}
31So far, so good, but now the versioning trouble begins. The author of Base produces a new version, giving it
32its own method F.
33// Author A
34namespace A
35{
36 |
public class Base |
// version 2 |
|
37 |
{ |
|
|
38 |
public virtual void F() { |
// added in version 2 |
|
39 |
System.Console.WriteLine("Base.F"); |
||
40 |
} |
|
|
41}
42}
43This new version of Base should be both source and binary compatible with the initial version. (If it weren’t
44possible to simply add a method then a base class could never evolve.) Unfortunately, the new F in Base
45makes the meaning of Derived’s F unclear. Did Derived mean to override Base’s F? This seems unlikely,
46since when Derived was compiled, Base did not even have an F! Further, if Derived’s F does override
47Base’s F, then it shall adhere to the contract specified by Base—a contract that was unspecified when
48Derived was written. In some cases, this is impossible. For example, Base’s F might require that overrides
49of it always call the base. Derived’s F could not possibly adhere to such a contract.
50C# addresses this versioning problem by requiring developers to state their intent clearly. In the original
51code example, the code was clear, since Base did not even have an F. Clearly, Derived’s F is intended as a
52new method rather than an override of a base method, since no base method named F exists.
53If Base adds an F and ships a new version, then the intent of a binary version of Derived is still clear—
54Derived’s F is semantically unrelated, and should not be treated as an override.
48
Chapter 8 Language overview
1However, when Derived is recompiled, the meaning is unclear—the author of Derived might intend its F
2to override Base’s F, or to hide it. Since the intent is unclear, the compiler produces a warning, and by
3default makes Derived’s F hide Base’s F. This course of action duplicates the semantics for the case in
4which Derived is not recompiled. The warning that is generated alerts Derived’s author to the presence of
5the F method in Base.
6If Derived’s F is semantically unrelated to Base’s F, then Derived’s author can express this intent—and,
7in effect, turn off the warning—by using the new keyword in the declaration of F.
8// Author A
9namespace A
10{
11 |
public class Base |
// version 2 |
12 |
{ |
|
13 |
public virtual void F() { // added in version 2 |
|
14 |
System.Console.WriteLine("Base.F"); |
|
15 |
} |
|
16}
17}
18// Author B
19namespace B
20{
21 |
class Derived: A.Base // version 2a: new |
22 |
{ |
23 |
new public virtual void F() { |
24 |
System.Console.WriteLine("Derived.F"); |
25 |
} |
26}
27}
28On the other hand, Derived’s author might investigate further, and decide that Derived’s F should
29override Base’s F. This intent can be specified by using the override keyword, as shown below.
30// Author A
31namespace A
32{
33 |
public class Base |
// version 2 |
34 |
{ |
|
35 |
public virtual void F() { // added in version 2 |
|
36 |
System.Console.WriteLine("Base.F"); |
|
37 |
} |
|
38}
39}
40// Author B
41namespace B
42{
43 |
class Derived: A.Base // version 2b: override |
44 |
{ |
45 |
public override void F() { |
46 |
base.F(); |
47 |
System.Console.WriteLine("Derived.F"); |
48 |
} |
49}
50}
51The author of Derived has one other option, and that is to change the name of F, thus completely avoiding
52the name collision. Although this change would break source and binary compatibility for Derived, the
53importance of this compatibility varies depending on the scenario. If Derived is not exposed to other
54programs, then changing the name of F is likely a good idea, as it would improve the readability of the
55program—there would no longer be any confusion about the meaning of F.
568.14 Extern Aliases
57By default types from all referenced assemblies and the current program are placed into a single namespace
58hierarchy. With only a single namespace hierarchy, it is not possible to reference types with the same fully
49
C# LANGUAGE SPECIFICATION
1qualified name from different assemblies, a situation that arises when types are independently given the
2same name, or when a program needs to reference several versions of the same assembly. Extern aliases
3make it possible to create and reference separate namespace hierarchies in such situations.
4Consider the following two assemblies:
5Assembly a1.dll:
6namespace N
7{
8 |
public class A {} |
9public class B {}
10}
11Assembly a2.dll:
12namespace N
13{
14 |
public class B {} |
15public class C {}
16}
17and the following program:
18class Test
19{
20 |
N.A a; |
// Ok |
21 |
N.B b; |
// Error |
22 |
N.C c; |
// Ok |
23}
24A command-line compiler might allow compilation of this program with a command-line something like
25this:
26csc /r:a1.dll /r:a2.dll test.cs
27where the types contained in a1.dll and a2.dll are all placed in the global namespace hierarchy, and an
28error occurs because the type N.B exists in both assemblies. With extern aliases, it becomes possible to place
29the types contained in a1.dll and a2.dll into separate namespace hierarchies.
30The following program declares and uses two extern aliases, X and Y, each of which represent the root of a
31distinct namespace hierarchy created from the types contained in one or more assemblies.
32extern alias X;
33extern alias Y;
34class Test
35{
36 |
X::N.A a; |
37 |
X::N.B b1; |
38 |
Y::N.B b2; |
39Y::N.C c;
40}
41The program declares the existence of the extern aliases X and Y, but the actual definitions of the aliases are
42external to the program. A command line compiler can enable the definition of the extern aliases X and Y
43such that the extern alias X is the root of a namespace hierarchy formed by the types in a1.dll and Y is the
44root of a namespace hierarchy formed by the types in a2.dll. A compiler might enable the above example
45with a command-line like:
46csc /r:X=a1.dll /r:Y=a2.dll test.cs
47The identically named N.B classes can now be referenced as X.N.B and Y.N.B, or, using the namespace
48alias qualifier, X::N.B and Y::N.B. An error occurs if a program declares an extern alias for which no
49external definition is provided.
50An extern alias can include multiple assemblies, and a particular assembly can be included in multiple extern
51aliases. For example, given the assembly
50
Chapter 8 Language overview
1Assembly a3.dll:
2namespace N
3{
4 |
public class D {} |
5public class E {}
6}
7a command line like
8csc /r:X=a1.dll /r:X=a3.dll /r:Y=a2.dll /r:Y=a3.dll test.cs
9might define the extern alias X to be the root of a namespace hierarchy formed by the types in a1.dll and
10a3.dll and Y to be the root of a namespace hierarchy formed by the types in a2.dll and a3.dll. Because
11of this definition, it is possible to refer to the class N.D in a3.dll as both X::N.D and Y::N.D.
12An assembly can be placed in the global namespace hierarchy even if it is also included in one or more
13extern aliases. For example, the command line
14csc /r:a1.dll /r:X=a1.dll /r:Y=a2.dll test.cs
15places the assembly a1.dll in both the global namespace hierarchy and the namespace hierarchy rooted by
16the extern alias X. Consequently, the class N.A can be referred to as N.A or X::N.A.
17It is possible to ensure that a lookup always starts at the root of the global namespace hierarchy by using the
18identifier global with the namespace alias qualifier, such as global::System.IO.Stream.
19A using directive may reference an extern alias that was defined in the same immediately enclosing
20namespace declaration or compilation unit. For example:
21extern alias X;
22using X::N;
23class Test
24{
25 |
A |
a; |
// |
X::N.A |
26 |
B |
b; |
// |
X::N.B |
27}
288.15 Attributes
29C# is an imperative language, but like all imperative languages it does have some declarative elements. For
30example, the accessibility of a method in a class is specified by declaring it public, protected,
31internal, protected internal, or private. C# generalizes this capability, so that programmers can
32invent new kinds of declarative information, attach this declarative information to various program entities,
33and retrieve this declarative information at run-time. Programs specify this additional declarative
34information by defining and using attributes (§24).
35For instance, a framework might define a HelpAttribute attribute that can be placed on program elements
36such as classes and methods, enabling developers to provide a mapping from program elements to
37documentation for them. The example
38using System;
39[AttributeUsage(AttributeTargets.All)]
40public class HelpAttribute: Attribute
41{
42 |
public HelpAttribute(string url) { |
43 |
this.url = url; |
44 |
} |
45 |
public string Topic = null; |
46 |
private string url; |
47 |
public string Url { |
48 |
get { return url; } |
49}
50}
51
