- •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
|
Chapter 26 Generics |
|
1 |
using X = N1.A.B; |
// Error, cannot name generic type |
2 |
using Y = N1.A<int>; |
// Ok, can name closed constructed type |
3}
4end example]
526.6 Generic methods
6A generic method is a method whose declaration includes a type-parameter-list (§17.5 and §20.2.1). Generic
7methods can be declared inside class, struct, or interface declarations, which can themselves be either
8generic or non-generic. If a generic method is declared inside a generic type declaration, the body of the
9method can refer to both the type parameters of the method, and the type parameters of the containing
10declaration.
11The type-parameter-list and type-parameter-constraints-clauses of a generic method declaration have the
12same syntax and purpose as in a generic type declaration. The method’s type-parameters are in scope
13throughout the method-declaration, and can be used to form types throughout that scope in return-type,
14method-body, and type-parameter-constraints-clauses but not in attributes.
15The name of a method type parameter cannot be the same as the name of an ordinary parameter in the same
16method.
17[Example: The following example finds the first element in an array, if any, that satisfies the given test
18delegate. (Generic delegates are described in §26.4.)
19public delegate bool Test<T>(T item);
20public class Finder
21{
22 |
public static T Find<T>(T[] items, Test<T> test) { |
23 |
foreach (T item in items) { |
24 |
if (test(item)) |
25 |
return item; |
26 |
} |
27 |
throw new InvalidOperationException("Item not found"); |
28}
29}
30end example]
3126.6.1 Generic method signatures
32For the purposes of signature comparisons any type-parameter-constraints-clauses are ignored, as are the
33names of the method’s type-parameters, but the number of generic type parameters is relevant, as are the
34ordinal positions of type-parameters in left-to-right ordering (§10.6). [Example: The following example
35shows how method signatures are affected by this rule:
36class A {}
37class B {}
38interface IX
39{
40 |
T F1<T>(T[] a, int i); |
// Error, both declarations have the same |
41 |
void F1<U>(U[] a, int i); |
// signature because return type and type |
42 |
|
// parameter names are not significant |
43 |
void F2<T>(int x); |
// Ok, the number of type parameters is |
44 |
void F2(int x); |
// part of the signature |
45 |
void F3<T>(T t) where T: A; |
// Error, constraints are not |
46 |
void F3<T>(T t) where T: B; |
// considered in signatures |
47}
48end example]
411
C# LANGUAGE SPECIFICATION
126.6.2 Virtual generic methods
2Generic methods can be declared using the abstract, virtual, and override modifiers. The signature
3matching rules described in §10.6 are used when matching methods for overriding or interface
4implementation. When a generic method overrides a generic method declared in a base class, or is an
5explicit interface member implementation of a method in a base interface, the method shall not specify any
6type-parameter-constraints-clauses. In these cases, the type parameters of the method inherit constraints
7from the method being overridden or implemented. [Example:
8abstract class Base
9{
10 |
public abstract T F<T,U>(T t, U u) where U : T; |
11public abstract T G<T>(T t) where T: IComparable;
12}
13interface I
14{
15bool M<T>(T a, T, b) where T : class;
16}
17class Derived: Base, I
18{
19
20 |
public override X F<X,Y>(X x, Y y) |
// Ok |
|
21 |
{ |
|
|
22 |
// The implicit constraint Y : X is inherited |
||
23 |
// so y is implicitly convertible to type X. |
||
24 |
return y; |
|
|
25 |
} |
|
|
26 |
public override T G<T>(T t) |
|
|
27 |
where T: IComparable |
// Error – constraints not allowed |
|
28 |
{…} |
|
|
29 |
bool I.M<U>(U a, U b) |
|
|
30 |
{ |
|
|
31 |
// The implicit constraint U : class is inherited |
||
32 |
// so a and b can be compared using the reference type |
||
33 |
// equality operators. |
|
|
34 |
return a == b; |
|
|
35}
36}
37The override of F is valid because type parameter names are permitted to differ. Within Derived.F, the
38type parameter Y implicitly has the constraint Y : X as inherited from Base.F. The override of G is invalid
39because overrides are not permitted to specify type parameter constraints. The explicit method
40implementation I.M in Derived implicitly inherits the U : class constraint from the interface method.
41When a generic method implicitly implements an interface method, the constraints given for each method
42type parameter shall be equivalent in both declarations (after any interface type parameters are replaced with
43the appropriate type arguments), where method type parameters are identified by ordinal positions, left to
44right. [Example:
45interface I<A, B, C>
46{
47 |
void |
F<T>(T |
t) |
where |
T |
: |
A; |
48 |
void |
G<T>(T |
t) |
where |
T |
: |
B; |
49void H<T>(T t) where T : C;
50}
51class Cls : I<object,Cls,string>
52{
53 |
public void F<T>(T t) {…} |
// Ok |
54 |
public void G<T>(T t) where T : Cls {…} |
// Ok |
55 |
public void H<T>(T t) where T : string {…} |
// Error |
56 |
} |
|
412
Chapter 26 Generics
1The method Cls.F<T> implicitly implements I<object,Cls,string>.F<T>. In this case, Cls.F<T> is
2not required (nor permitted) to specify the constraint T : object since object is an implicit constraint on
3all type parameters. The method Cls.G<T> implicitly implements I<object,Cls,string>.G<T>
4because the constraints match those in the interface, after the interface type parameters are replaced with the
5corresponding type arguments. The constraint for method Cls.H<T> is an error since sealed types (string in
6this case) cannot be used as constraints. Omitting the constraint would also be an error since constraints of
7implicit interface method implementations are required to match. Thus, it’s impossible to implicitly
8implement I<object,Cls,string>.H<T>. This interface method needs to be implemented using an
9explicit interface member implementation:
10class Cls : I<object,Cls,string>
11{
12 |
… |
13 |
public void H<U>(U u) where U : class {…} |
14 |
void I<object,Cls,string>.H<T>(T t) |
15 |
{ |
16 |
string s = t; // Ok |
17 |
H<T>(t); |
18}
19}
20In this example, the explicit interface member implementation invokes a public method having strictly
21weaker constraints. Note that the assignment from t to s is valid since T inherits a constraint of
22T : string, even though this constraint is not expressible in source code. end example]
2326.6.3 Calling generic methods
24A generic method invocation can explicitly specify a type argument list, or it can omit the type argument list
25and rely on type inference to determine the type arguments. The exact compile-time processing of a method
26invocation, including a generic method invocation, is described in §14.5.5.1. When a generic method is
27invoked without a type argument list, type inference takes place as described in §26.6.4.
28[Example: The following example shows how overload resolution occurs after type inference and after type
29arguments are substituted into the parameter list:
30class Test
31{
32 |
static void F<T>(int x, T y) { |
|
33 |
Console.WriteLine("one"); |
|
34 |
} |
|
35 |
static void F<T>(T x, long y) { |
|
36 |
Console.WriteLine("two"); |
|
37 |
} |
|
38 |
static void Main() { |
|
39 |
F<int>(5, 324); |
// Ok, prints "one" |
40 |
F<byte>(5, 324); |
// Ok, prints "two" |
41 |
F<double>(5, 324); |
// Error, ambiguous |
42 |
F(5, 324); |
// Ok, prints "one" |
43 |
F(5, 324L); |
// Error, ambiguous |
44}
45}
46end example]
4726.6.4 Inference of type arguments
48When a generic method is called without specifying type arguments, a type inference process attempts to
49infer type arguments for the call. The presence of type inference allows a more convenient syntax to be used
50for calling a generic method, and allows the programmer to avoid specifying redundant type information.
51[Example: Given the method declaration:
52class Chooser
53{
54 |
static Random rand = new Random(); |
413
|
C# LANGUAGE SPECIFICATION |
1 |
public static T Choose<T>(T first, T second) { |
2 |
return (rand.Next(2) == 0) ? first : second; |
3}
4}
5it is possible to invoke the Choose method without explicitly specifying a type argument:
6 |
int i = Chooser.Choose(5, 213); |
// |
Calls |
Choose<int> |
7 |
string s = Chooser.Choose("foo", "bar"); |
// |
Calls |
Choose<string> |
8Through type inference, the type arguments int and string are determined from the arguments to the
9method. end example]
10Type inference occurs as part of the compile-time processing of a method invocation (§14.5.5.1) and takes
11place before the overload resolution step of the invocation. When a particular method group is specified in a
12method invocation, and no type arguments are specified as part of the method invocation, type inference is
13applied to each generic method in the method group. If type inference succeeds, then the inferred type
14arguments are used to determine the types of arguments for subsequent overload resolution. If overload
15resolution chooses a generic method as the one to invoke, then the inferred type arguments are used as the
16actual type arguments for the invocation. If type inference for a particular method fails, that method does not
17participate in overload resolution. The failure of type inference, in and of itself, does not cause a compile-
18time error. However, it often leads to a compile-time error when overload resolution then fails to find any
19applicable methods.
20If the supplied number of arguments is different than the number of parameters in the method, then inference
21immediately fails. Otherwise, type inference first occurs independently for each regular argument that is
22supplied to the method. Assume this argument has type A, and the corresponding parameter has type P. Type
23inferences are produced by relating the types A and P according to the following steps:
24• Nothing is inferred from the argument (but type inference succeeds) if any of the following are true:
25o P does not involve any method type parameters.
26o The argument is the null literal.
27o The argument is an anonymous method.
28o The argument is a method group.
29• If P is an array type, and A is an array type of the same rank, then replace A and P, respectively, with the
30element types of A and P, and repeat this step.
31• If P is an array type, and A is not an array type of the same rank, then type inference fails for the generic
32method.
33• If P is a method type parameter, then type inference succeeds for this argument, and A is the type
34inferred for that type parameter.
35• Otherwise, P shall be a constructed type. If, for each method type parameter MX that occurs in P, exactly
36one type TX can be determined such that replacing each MX with each TX produces a type to which A is
37convertible by a standard implicit conversion, then inferencing succeeds for this argument, and each TX
38is the type inferred for each MX. Method type parameter constraints, if any, are ignored for the purpose of
39type inference. If, for a given MX, no TX exists, or more than one TX exists, then type inference fails for
40the generic method (a situation where more than one TX exists can only occur if P is a generic interface
41type and A implements multiple constructed versions of that interface).
42If all of the method arguments are processed successfully by the above algorithm, then all inferences that
43were produced from the arguments are pooled. Type inference is said to have succeeded for the given
44generic method and argument list if both of the following are true::
45• Each type parameter of the method had a type argument inferred for it (in short, the set of inferences is
46complete).
414
