Connected to Generics Five
20.8 Expressions and Statements
The operations of some expressions and statements have been modified for generics. This section describes these changes.
20.8.1 Default value expression
Default value expression is used to obtain the default value of a type (§5.2). Usually a default value expression is used for type parameters, because if the type parameter is a value type or a reference type, it may not already be there. (There is no conversion from null type to type parameter.)
primary-no-array-creation-expression:(基本无数组创建表达式:) … default-value-expression(默认值表达式) default-value-expression:(默认值表达式:) primary-expression . default (基本表达式 .default) predefined-type . default(预定义类型. default)
If a basic expression is used in a default value expression, and the basic expression cannot be divided into a type, then A compile-time error will occur. However the rules described in §7.5.4.1 also apply to components forming E.default.
If the left-hand side of the default value expression is evaluated at runtime against a reference type, the result is null converted to that type. If the left-hand side of a default value expression is evaluated at runtime against a value type, the result is the value type's default value (§4.1.2).
If the type is a reference type or type parameter with a class constraint, the default value expression is a constant expression (§7.15). Additionally, the default value expression is a constant expression if the type is one of the following: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool.
20.8.2 Object creation expression
The type of common expressions of objects can be a type parameter. When a type parameter is specified as a type in an object creation expression, the following two conditions must be met, otherwise a compile-time error will occur
The actual parameter list must be deleted
Must be specified for the type parameter Constructor constraints of the new() form
You can execute an object creation expression by creating an instance of the run-time type to which the type parameter is bound and calling the default constructor of that type. Runtime types can be reference or value types.
20.8.3 Type of operator
The typeof operator can be used for type parameters. The result is a System.Type object of the runtime type that is bound to the type parameter. The typeof operator can also be used to construct types.
class X <T> { public static void PrintTypes() { Console.WriteLine(typeof(T).FullName); Console.WriteLine(typeof(X<X<T>>).FullName); } } class M { static void Main() { X<int>.PrintTypes(); } }
The previous program will print as follows.
System.Int32 X<X<Sytem.Int32>> Typeof运算符不能用于没有指定类型实参的泛型类型声明的名字。 class X<T>{…} class M { static void Main() { Type t = typeof(X); //错误,X需要类型实参 } }
20.8.4 Reference equality operator
If T is constrained by a class constraint, the reference type equality operator can be used to compare the values of the type parameter T.
The usage of the reference type equality operator allows the actual parameters of the type parameter T to be easily compared with other actual parameters that are null, even if T has no class constraints. At runtime, if T is a value type, the result of the comparison will be false.
The following example checks whether the actual parameter of an unconstrained type parameter type is null.
class C<T> { void F(T x) { if(x==null) thow new ArgumentNullException(); … } }
Even if T can represent a value type, the x==null construct is allowed, and when T is a value type, its result is simply defined as false.
20.8.5 is运算符
在开放类型上的is运算符操作遵循通常的规则(§7.9.9)。如果e或T的编译时类型是一个开放类型,那么在运行时对于e和T将总是执行动态类型检查。
20.8.6as运算符
只要T有一个类约束,类型参数T可被用在as运算符的右边。这种限制是需要的,因为值null可能被作为运算符的结果返回。
class X { public T F<T>(object o) where T:Attribute { return o as T; //ok,T有一个类约束 } public T G<T>(object o) { return o as T; //错误,T没有约束 }
}
在as运算符(§7.9.10)的当前规范中,对于表达式e as T最后一点表明,如果从e的编译时类型到T,不存在有效的显式引用转换,将会出现编译时错误。对于泛型,这条规则稍微作了修改。如果E的编译时类型或T是一个开放类型,在这种情况下将不会出现编译时错误;相反,运行时检查将会执行。
20.8.7异常语句
对于开放类型,throw(§8.9.5)和try(§8.10)的通常规则是适用的。
只要类型参数具有System.Exeption异常(或子类具有)作为类约束,那么throw语句可以被用作其类型有一个类型参数给定的表达式。
只要类型参数System.Exception(或子类子类具有)作为类约束,那么在catch语句中的命名的类型可能是一个类型参数。
20.8.8 lock语句
lock语句可以被用作其类型由一个类型参数给定的表达式。如果表达式的运行时类型是一个值类型,lock将没有效果(因为对于装箱值不能有任何其他的引用)。
20.8.9 using 语句
using 语句(§8.13)遵循通常的规则:表达式必须被隐式的转换到System.IDisposable。如果类型参数通过System.IDisposable而约束,那么该类型的表达式可以使用using 语句。
20.8.10 foreach语句
给定如下形式的foreach语句
foreach(ElementType element in collection) statement
如果集合表达式是一个没有实现集合模式的类型,但为每个类型T实现了构造接口System.Collections.Generic.IEnumerable
IEnumerator<T> enumerator = ((IEnuemrable<T>)(collection).GetEnumerator(); try { where (enumerator.MoveNext()){ ElementType element = (ElementType)enumerator.Current; statement; } } finally{ enumerator.Dispose(); }
20.9查找规则修订
泛型修改了用于查找和绑定名字的某些基本规则。下面几节在考虑泛型的情况下,重新叙述了所有的基本名字查找规则。
20.9.1命名空间和类型名字
The following content can replace §3.8.
There are several contexts in C# programs that require specifying namespaces or type names. Any form of name may consist of one or more identifiers separated by "." marks.
namespace-name: (namespace name:)
namespace-or-type-name (namespace or type name)
type-name: (type name:)
namespace-or-type -name (namespace or type name)
namespace-or-type-name: (namespace or type name:)
identifier type-argument-list opt (identifier type argument list optional)
namespace-or-type-name . identifier type-argument-list opt (namespace or type name. Identifier type argument list optional)
The namespace name is the namespace name or type name that refers to the namespace ( namespace-or-type-name). See the decisions described below. The namespace or type name of a namespace name must refer to a namespace, otherwise a compile-time error occurs. There cannot be type arguments in a namespace name (only types can have type arguments).
The type name is a namespace or type name (namespace-or-type-name) that refers to the type. See the decision described below. A type name's namespace or type name must refer to a type, otherwise a compile-time error occurs.
The meaning of a namespace or type name is determined as follows.
If the namespace or type name is of the form I or I
- If a namespace or type name appears within a generic method declaration, and the declaration includes a type parameter named by I that does not specify a type argument list, then a namespace or type name reference The type parameter.
- Otherwise, if a namespace or type name appears within a type declaration, then for each instance of type T (
u If no declaration of type parameter T containing the name given by I is specified, and there is no type argument list, then the namespace or type name Reference that type parameter.
u Otherwise, if I is the name of an accessible member of T, and if that member is a type with a matching number of type parameters, then the namespace or type name refers to type T.I or type T .I
- Otherwise, for each namespace N starting with the namespace or type name appearing in it, and continuing with each enclosing namespace (if any), then ending with the global namespace, the following Steps will be calculated until the entity is located.
u If I is a name in a namespace in N and no type argument list is specified, then the namespace or type name refers to that namespace.
u Otherwise, if I is the name of an accessible type in N with a matching number of type parameters, then the namespace or type name refers to a type constructed with the given type arguments.
u Otherwise, if the namespace or type name appears in a position enclosed by a namespace declaration of N
- if the namespace declaration contains a using alias directive with a name given by I, and I has an import name space or type, and no argument list is specified, then the namespace or type name refers to that namespace or type
- otherwise, if the namespace imported by the using namespace directive of the namespace declaration happens to contain a namespace with I given A named type that matches the number of type parameters, then the namespace or type name refers to the type constructed from the given type arguments.
- Otherwise, the namespace or type name is ambiguous if the namespace imported by the using namespace directive in the namespace declaration contains more than one type with the given name matching the number of type arguments, and will result in an error.
- Otherwise, the namespace or type name is undefined, and a compile-time error occurs.
l Otherwise, the namespace or type name is of the form N.I or N.I
- If N refers to a namespace, and if I is a namespace name embedded in N and no type argument list is specified, then the namespace or type name refers to the embedded namespace.
- Otherwise, if N refers to a namespace, and I is the name of a type accessible in N with a matching number of type arguments, then the namespace or type name refers to the type constructed from the given type arguments.
- Otherwise, if N refers to a class or struct type, and I is the name of an accessible type embedded in N with matching type parameters, then the namespace or type name reference is constructed from the given arguments. That type.
- Otherwise, N.I is an invalid namespace name, and a compile-time error will occur.
20.9.2 Member lookup
The following content can be replaced§7.3
Member lookup is a process that determines the type based on its meaning in the context. Within an expression, member lookup can occur as a simple name evaluation or as a member access (§20.9.4).
The member search of name N in type T is determined according to the following rules.
First, a group of accessible members named N is determined.
- If T is a type parameter, then in each type specified as a class constraint or interface constraint of T, together with the set of named members of N in object, this set is named. Access members of the federation.
- Otherwise, this set consists of all named accessible members of N in T, including inherited members and named accessible members of N in object. If T is a constructed type, the set of members is obtained by substituting the type arguments as described in §20.5.4. Members including the override modifier will be expelled from the collection.
Next, members that are hidden through other members will be removed from the set. For each member S.M in a set, S is the type in which M is declared, the following rules apply
- If M is a constant, field, property, event, or enumeration member, then in All members declared in base classes of S will be removed from this collection.
- If M is a type declaration, then all non-type declarations in the base class of S are removed from the set, and all type declarations of M with the same number of type parameters as S declared in the base type , will be removed from the collection.
- If M is a method, then all non-method members declared in the base class of S will be removed from this set, and all methods of M declared in the base type as S will have the same signature. will be removed from this collection.
Next, the interface members hidden through class members will be removed from the collection. This step is only valid when T is a type parameter and T has class constraints and multiple interface constraints. For each member S.M in the collection, where S is the type in which M is declared, if S is a class declaration rather than an object, the following rules apply
- If M is a constant, field, property, event, enumeration member or type declaration, then all members declared in the interface declaration will be removed from this collection.
- If M is a method, then all non-method members declared in the interface type will be removed from this set, and all methods with the same signature of M declared in the interface as S will be removed from this set. Removed from this collection.
Finally, after deleting the hidden members, the result of the search will be determined
- If a collection consists of a single member, not a type or method, then this member is the result of the search.
- Otherwise, if the set contains only methods, then this set of methods is the result of the search.
- Otherwise, if the set contains only type declarations, then this set of type declarations is in the result of the member lookup.
- Otherwise, the search is ambiguous and a compile-time error will occur.
For types, rather than type parameters and member lookups of interfaces, member lookups in interfaces are strictly single inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), lookup The effect of the rule is only that derived members hide base class members with the same name and signature. This single inheritance lookup is very explicit. Possible ambiguities in member lookup arise from the multiple inheritance interface described in §13.2.5
20.9.3 Simple names
The following content can replace §7.5.2.
A simple name consists of an identifier, followed by an optional list of type parameters.
simple-name: (simple name:)
identifier type-argument-list opt (identifier type argument list optional)
For simple names of the form I or I
If the simple name appears within a block, and if the block's local variable declaration space contains a local variable or parameter with a name given by I, then the simple name refers to the local variable and parameter and acts as a variable And be classified. If a type argument list is specified, a compile-time error will occur.
If the simple name appears within the body of a generic method declaration, and if the declaration contains a type parameter with the name given by I, then the simple name refers to that type parameter. If only the type argument list is specified, it will A compile-time error occurred.
Otherwise, for each instance of type T that begins with an instance type declared by the directly enclosing class, structure, or enumeration, continue with the instance type declared by each enclosing outer class or structure, if any.
- If the declaration of T includes a type parameter named by I, then the simple name refers to the type parameter. If a type argument list is specified, a compile-time error will occur.
- Otherwise, if a member lookup of I in T produces a match
u If T is an instance type of a directly enclosing class or struct type, and the lookup identifies one or more methods, the result will be a match with The method group associated with this expression. If a type argument list is specified, it is used in a generic method call (§20.6.3).
u If T is an instance type of a directly enclosing class or struct type, if the lookup identifies an instance member and the reference occurs within a block of an instance constructor, instance method, or an instance accessor, the result will be the same as this Member access of the .I form is similar. If a type argument is specified, a compile-time error will occur.
u Otherwise, the result is similar to a member access of the form T.I or T.I
Otherwise, for namespace N with each namespace in which the simple name appears, continue with each enclosing namespace (if any), ending with the global namespace, The following steps will be calculated until an entity is located.
- If I is the name of a namespace in N and no type argument list is specified, then the simple name will refer to that namespace.
- Otherwise, if I is the name of an accessible type in N with a matching number of type parameters, then the simple type refers to that type constructed from the given type arguments.
u If a namespace declaration contains a using alias directive associated with a name given by I, where I is an imported namespace or type, and no type argument list is specified, then the simple name refers to the namespace or type.
u Otherwise, if the namespace imported by the using namespace directive of the namespace declaration happens to contain a type named by I that matches the number of type arguments, then the simple name reference is constructed from the given type arguments. type.
u Otherwise, if the namespace imported by the using namespace directive of the namespace declaration contains multiple types whose names are given by I and match the number of type parameters, then the simple name is ambiguous and will result in a compile-time error.
l Otherwise, the name given by the simple name is undefined and will result in a compile-time error.
20.9.4 Member Access
The following content can replace §7.5.4.
Member access consists of a basic expression or predefined type, followed by a "." mark, followed by an identifier, and then an optional list of type arguments.
member-access: (member access:)
primary-expression . identifier type-argument-list opt (basic expression. Identifier type argument list optional)
predefined-type . identifier type- argument-list opt (predefined type. Identifier type argument list optional) Predefined type: one of the following
bool byte char decimal double float int long
object sbyte short string uint ulong ushort
For member access of the form E.I or E.I
If E is a namespace and I is the name of a nested namespace in E, and no type arguments are specified, the result is this namespace.
If E is a namespace, I is the name of a type accessible in E, and E matches the number of type parameters, then the result is the type constructed from the given type arguments.
If E is a predefined type or a basic expression classified as a type, if E is not a type parameter, and if a member lookup of I in E yields a match, then E.I is evaluated and classified as follows .
- If I identifies one or more type declarations, that type declaration is determined using the same number (possibly zero) of type parameters as if provided in the type arguments. The result is a type constructed from the given type arguments. If the type declaration does not match the number of type parameters, a compile-time error will occur.
- If I identifies one or more methods, the result is a method group with no associated instance expression. If a type argument list is specified, it will be used in generic method calls (§20.6.3).
- If I identifies a static property, static field, static event, constant, or an enumeration member, a compile-time error will occur if a type argument list is specified.
- If I identifies a static property, the result is a property access with an unassociated instance expression.
- If I identifies a static field
u If the field is read-only and the reference occurs outside the static constructor of the class or structure, the field will be declared here. Then the result is a value, which is the value of the static field I in E.
u Otherwise, the result is a variable, which is the static field I in E.
- If I identifies a static event
u If the reference occurs in the class or structure in which the event is declared, and the event is declared without an event-accessor-declaration (§10.7), Then E.I is treated as if I were a static field.
u Otherwise, the result is an event access without an associated instance expression.
- If I identifies a constant, then the result is the value, which is the value of the constant.
- If I identifies an enumeration member, the result is a value, which is the value of the enumeration member.
- Otherwise, E.I is an invalid member reference and will cause a compile-time error.
If E is a property access, indexer access, variable, or value whose type is T, and a member lookup of I in T produces a match, then E.I is computed and classified as follows .
- First, if E is a property or indexer access, then the value of the property or indexer access is obtained (§7.1.1), and E is reclassified as a value.
- If I identifies one or more methods, the result is a method group with an associated instance expression of E. If a type argument list is specified, it will be used in generic method calls (§20.6.3).
- If I identifies an instance property, instance field, or instance event, a compile-time error will occur if a type argument list is specified.
- If I identifies an instance property, the result is an instance expression with an associated E.
- If T is a class type and I identifies an instance field of a class type
u If the value of E is null, System.NullReferenceException will be thrown.
u Otherwise, if the field is read-only and the reference occurs outside an instance constructor of the class in which the field is declared, then the result is the value that is the value of I in the object referenced by E.
u Otherwise, the result is the variable, which is the field I in the object referenced by E.
- If T is a struct type and I identifies an instance field of that struct type
u if E is a value, or if the field is read-only and the reference appears in the instance constructor of the struct where the field is declared otherwise, the result is a value, that is, the value of field I in the structure instance given by E.
u Otherwise, the result is a variable, that is, a field I in the instance of the structure given by E;
- if I identifies an instance event
u if the reference appears in the class or structure in which the event was declared within, and the event is declared without an event accessor declaration, then E.I is treated as if I were an instance field.
u Otherwise, the result is an instance expression with an associated E.
Otherwise, E.I is an invalid member reference, which will cause a compile-time error.
20.9.5 Method Call
The following content can replace the compile-time processing part describing method call in §7.5.5.1.
Compile-time processing of method calls in the form of M(A), where M is a method group (may contain a type argument list), A is an optional argument list, consisting of the following steps.
The candidate set of method calls is constructed. For each method F
associated with method group M - if F is non-generic, F is a candidate when
u M has no type argument list, and
u for A (§7.4.2.1) ,F is applicable.
- If F is a generic and M has no type argument list, F is a candidate when , and
u Once the inferred type arguments replace the corresponding method type parameters, the parameter list of F is applicable to A, and
u After the type arguments are replaced, the parameter list of F is as applicable as possible Non-generic methods declared as F in the same type are different in their extended form (§7.4.2.1).
- If F is a generic and M includes a type argument list, F is a candidate when the following holds
u F has the same number of method types as provided in the type argument list parameters, and
u Once the type arguments are substituted for the corresponding method type parameters, the parameter list of F is applicable to A (§7.4.2.1).
The set of candidate methods is reduced to contain only methods derived from deeply derived types: for every C.F method in the set, C is the type in which F is declared, and the base type of C is declared in All methods of are removed from the collection.
If the result set of the candidate method is empty, then no applicable method exists and a compile-time error will occur. If the candidate method is not declared in the same type, the method call will be ambiguous and a compile-time error will occur (this latter case is only possible for a method in an interface with multiple direct base interfaces) call, as described in §13.2.5).
Final verification of the chosen best method is performed
- The method is valid in the context of a method group: if the method is a static method, the method group must be derived from the simple name or Member access. If the best method is an instance method, the method group must be accessible via a variable or value or base-access derived from simple name or member access. If none of these requirements are met, a compile-time error will occur.
- If the best method is a generic method, the type arguments (supplied or inferred) will be checked against the constraints declared on the generic method. If any type argument does not satisfy the constraints of the corresponding type parameter, a compile-time error is generated.
Once the method is selected and verified according to the previous steps, the actual runtime call will be processed according to the function member call rules in §7.4.
The following content can replace the compile-time processing part of the delegate creation expression in §7.5.10.3.
Compile-time processing of delegate creation expressions in the form of new D(E), where D is a delegate type and E is an expression, consists of the following steps.
If E is a method group
- Corresponding to a method call of the form E(A), a single method call will be selected.
u The parameter type and modifier (ref or out) of D are used as the modifier of the actual parameter type and the actual parameter list A.
u Conversions are not considered in applicable testing and type inference. In instances where implicit conversion is sufficient, the type requirements are identical.
u The overload decision step will not be executed. Instead, the set of candidates must contain exactly one D-compatible method (then using type arguments instead of type parameters), and this method will become the method referenced by the newly created delegate. If no matching method exists, or more than one matching method exists, a compile-time error occurs.
- If the selected method is an instance method, the instance expression associated with E determines the target object of the delegate.
- The result is a value of type D, which refers to the selected method and the newly created delegate of the target object.
Otherwise, E is a value of a delegate type
- D and E must be compatible; otherwise a compile-time error occurs.
- The result is a value of type D, i.e. a newly created delegate referencing the same call list as E.
Otherwise, the delegate creation expression is invalid and a compile-time error occurs.
20.10 Right shift syntax changes
Generics use "<" and ">" characters to separate type parameters and type arguments (similar to C++'s template syntax). Constructed types can sometimes be nested, such as List
In order to maintain a simple lexicon for these neutral constructs, the ">>" and ">>=" markers have been removed from the lexicon and replaced by right-shift and right-shift assignment productions.
Operator or punctuation: one
{ } [ ] ( ) . , : ;
+ - * / % & | ^ ! ~
= < > ? ++ -- && || == ->
!= <= >= += -= *= /= %= &= |=
^= << <<=
right -shift: (right shift:)
> >
right-shift-assignment: (right shift assignment)
> >=
Unlike other productions in the syntax, No characters of any kind (even spaces) are allowed between the tokens of right-shift and right-shift assignment productions.
The following productions are modified using right shift or right shift assignment.
shift-expression: (shift expression:)
additive-expression (additional expression)
shift-expression << additive-expression (shift expression<< additional expression )
shift-expression right-shift additive-expression (shift expression right-shift additional expression)
assignment-operator: (assignment operator:)
=
+=
-=
*=
/=
%=
&=
|=
^=
<<=
right-shift-assignment
overloadable-binary-operator: (Overloadable binary operator:)
+
-
*
/
%
&
|
^
<<
right-shift
==
!=
>
<
>=
<=
(End of generics)
****************************
The above is the C# 2.0 Specification (Generic 6) content, please pay attention to the PHP Chinese website (www.php.cn) for more related content!