接泛型五
20.8 表达式和语句
某些表达式和语句的操作针对泛型进行了修改。这一节将介绍这些改变。
20.8.1默认值表达式
默认值表达式被用于获得一个类型的默认值(§5.2)。通常一个默认值表达式被用于类型参数,因为如果类型参数的是一个值类型或引用类型,它可能不是已经的。(不存在从 null 类型到类型参数的转换。)
primary-no-array-creation-expression: (基本无数组创建表达式:)
…
default-value-expression (默认值表达式)
default-value-expression: (默认值表达式:)
primary-expression . default (基本表达式 .default)
predefined-type . default (预定义类型. default)
如果一个基本表达式在一个默认值表达式中被使用,并且这个基本表达式不能分为一个类型,那么将会出现一个编译时错误。然而在§7.5.4.1中描述的规则也适用于形成 E.default 的构件。
如果默认值表达式的左边针对一个引用类型在运行时被计算,结果是将null转换到那个类型。如果默认值表达式的左边针对一个值类型在运行时被计算,其结果是值类型的默认值(§4.1.2)。
如果类型是一个具有类约束的引用类型或类型参数,默认值表达式是一个常量表达式(§7.15)。此外,如果类型是下列值之一,默认值表达式是一个常量表达式: sbyte 、 byte 、 short 、 ushort 、 int 、 uint 、 long 、 ulong 、 char 、 float 、 double 、 decimal 或 bool 。
20.8.2对象创建表达式
对象常见表达式的类型可以是一个类型参数。当类型参数被作为对象创建表达式中的类型而指定时,下面两个条件必须具备,否则将会出现编译时错误
- 实参列表必须删除
- 必须为类型参数而指定 new ()形式的构造函数约束
通过创建一个类型参数被绑定到的运行时类型的实例,并调用该类型的默认构造函数,就可以执行对象创建表达式。运行时类型可以是引用或者值类型。
20.8.3运算符的类型
typeof 运算符可以用于类型参数。其结果是被绑定到类型参数的运行时类型的 System.Type 对象。 typeof 运算符也可以被用于构造类型。
class X
1<t>
2
3{
4
5public static void PrintTypes()
6
7{
8
9Console.WriteLine(typeof(T).FullName);
10
11Console.WriteLine(typeof(X<x<t>>).FullName);
12
13}
14
15}
16
17class M
18
19{
20
21static void Main()
22
23{
24
25X<int>.PrintTypes();
26
27}
28
29}
30
31先前的程序将打印如下。
32
33System.Int32
34
35X<x<sytem.int32>>
36
37Typeof 运算符不能用于没有指定类型实参的泛型类型声明的名字。
38
39class X<t>{…}
40
41class M
42
43{
44
45static void Main()
46
47{
48
49Type t = typeof(X); // 错误, X 需要类型实参
50
51}
52
53}
54
55### 20.8.4引用相等运算符
56
57如果T由一个类约束而约束,引用类型相等运算符可以被用于比较类型参数T的值。
58
59引用类型相等运算符的用法可以让类型参数T的实参很容易地与其他为 null 的实参进行比较,即使T没有类约束也如此。在运行时,如果T是一个值类型,比较的结果将是 false 。
60
61
62
63
64下面的例子检查一个非约束类型参数类型的实参是否是 null 。
65
66class C<t>
67
68{
69
70void F(T x)
71
72{
73
74if(x==null) thow new ArgumentNullException();
75
76…
77
78}
79
80}
81
82即使是T可以表示一个值类型, x==null 构件也是允许的,并且当T是值类型时,其结果被简单的定义为 false 。
83
84### 20.8.5 is运算符
85
86在开放类型上的is运算符操作遵循通常的规则(§7.9.9)。如果e或T的编译时类型是一个开放类型,那么在运行时对于e和T将总是执行动态类型检查。
87
88### 20.8.6as运算符
89
90只要T有一个类约束,类型参数T可被用在 as 运算符的右边。这种限制是需要的,因为值 null 可能被作为运算符的结果返回。
91
92class X
93
94{
95
96public T F<t>(object o) where T:Attribute
97
98{
99
100return o as T; //ok,T 有一个类约束
101
102}
103
104public T G<t>(object o)
105
106{
107
108return o as T; // 错误, T 没有约束
109
110}
111
112}
113
114在as运算符(§7.9.10)的当前规范中,对于表达式 e as T 最后一点表明,如果从e的编译时类型到T,不存在有效的显式引用转换,将会出现编译时错误。对于泛型,这条规则稍微作了修改。如果E的编译时类型或T是一个开放类型,在这种情况下将不会出现编译时错误;相反,运行时检查将会执行。
115
116### 20.8.7异常语句
117
118对于开放类型, throw (§8.9.5)和 try (§8.10)的通常规则是适用的。
119
120
121
122
123 * 只要类型参数具有 System.Exeption 异常(或子类具有)作为类约束,那么 throw 语句可以被用作其类型有一个类型参数给定的表达式。
124 * 只要类型参数 System.Exception (或子类子类具有)作为类约束,那么在 catch 语句中的命名的类型可能是一个类型参数。
125
126
127
128### 20.8.8 lock语句
129
130lock 语句可以被用作其类型由一个类型参数给定的表达式。如果表达式的运行时类型是一个值类型, lock 将没有效果(因为对于装箱值不能有任何其他的引用)。
131
132### 20.8.9 using 语句
133
134using 语句(§8.13)遵循通常的规则:表达式必须被隐式的转换到 System.IDisposable 。如果类型参数通过 System.IDisposable 而约束,那么该类型的表达式可以使用 using 语句。
135
136### 20.8.10 foreach语句
137
138给定如下形式的 foreach 语句
139
140foreach(ElementType element in collection) statement
141
142如果集合表达式是一个没有实现集合模式的类型,但为每个类型T实现了构造接口 System.Collections.Generic.IEnumerable<t> ,那么foreach语句的扩展是
143
144IEnumerator<t> enumerator = ((IEnuemrable<t>)(collection).GetEnumerator();
145
146try
147
148{
149
150where (enumerator.MoveNext()){
151
152ElementType element = (ElementType)enumerator.Current;
153
154statement;
155
156}
157
158}
159
160finally{
161
162enumerator.Dispose();
163
164}
165
166## 20.9 查找规则修订
167
168泛型修改了用于查找和绑定名字的某些基本规则。下面几节在考虑泛型的情况下,重新叙述了所有的基本名字查找规则。
169
170**
171**
172
173### 20.9.1命名空间和类型名字
174
175如下内容可替换§3.8。
176
177在C#程序中有几个上下文需要指定命名空间或者类型名字。任何形式的名字都可以由一个或多个由“.”标记分隔的标识符号组成。
178
179_namespace-name:_ (命名空间名字:)
180
181_namespace-or-type-name_ (命名空间或类型名字)
182
183_type-name:_ (类型名字:)
184
185_namespace-or-type-name_ (命名空间或类型名字)
186
187_namespace-or-type-name:_ (命名空间或类型名字:)
188
189_identifier type-argument-list_ opt (标识符 类型实参列表 可选 )
190
191_namespace-or-type-name . identifier type-argument-list_ opt ( 命名空间或类型名字. 标识符 类型实参列表 可选 )
192
193命名空间名字是引用命名空间的命名空间名字或类型名字( namespace-or-type-name )。见下面所描述的决策,命名空间名字的命名空间或类型名字必须引用一个命名空间,否则出现编译时错误。在一个命名空间名字中不能有类型实参(只有类型可以具有类型实参)。
194
195类型名字是一个引用类型的命名空间或类型名字( namespace-or-type-name )。见下面所描述的决策,类型名字的命名空间或类型名字必须引用一个类型,否则出现编译时错误。
196
197命名空间或类型名字的意义按如下确定。
198
199 * 如果命名空间或类型名字是I或I<a ,…,a="" 1="" n="">的形式,其中I 是一个单一标识符,并且<a ,…,a="" 1="" n="">是一个可选类型实参列表。
200
201
202
203\- 如果命名空间或类型名字出现在泛型方法声明之内,并且该声明包括一个由I给定名字的没有指定类型实参列表的类型参数,那么命名空间或类型名字引用该类型参数。
204
205\- 否则,如果命名空间或类型名字出现在类型声明之内,那么对于类型T的每个实例(<a ,…,a="" 1="" n="">20.1.2),以那个类型声明的实例类型开始,并继续采用每个封闭类或结构类型声明(如果有的话)
206
207u 如果没有指定包含由I给定名字,并且没有类型实参列表的类型参数T的声明,那么命名空间或类型名字引用那个类型参数。
208
209
210
211
212u 否则,如果I是T中可访问成员的名字,并且如果那个成员是一个带有匹配类型参数数量的类型,那么命名空间或类型名字引用类型T.I或者类型T.I<a ,…a="" 1="" n="">。注意当确定命名空间或类型名字的意义时,无类型成员(常数、字段、方法、属性、索引器、运算符、实例构造函数、析构函数和静态构造函数)和带有不同数量的类型参数成员将会被忽略。
213
214\- 否则,对于在以命名空间或类型名字出现的命名空间开始的每个命名空间N,并继续使用每个封闭命名空间(如果有的话),然后以全局命名空间结束,下面的步骤将会被计算,直到该实体被定位。
215
216u 如果I 是在N中的命名空间中的名字,并且没有指定类型实参列表,那么命名空间或类型名字引用该个命名空间。
217
218u 否则,如果I是在N中带有匹配类型参数数量的可访问类型的名字,那么命名空间或类型名字引用使用给定类型实参构造的类型。
219
220u 否则,如果命名空间或类型名字出现的位置,由N的命名空间声明所封闭
221
222\- 如果命名空间声明包含一个由I 给定名字的using 别名指令,而I带有导入命名空间或类型,并且没有指定参数列表,那么命名空间或类型名字引用该命名空间或者类型
223
224\- 否则,如果由命名空间声明的using命名空间指示符导入的命名空间,恰好包含带有I 给定名字的,匹配类型参数数量的类型,那么命名空间或类型名字引用由给定类型实参构造的这个类型。
225
226\- 否则,如果由命名空间声明的using 命名空间指示符导入的命名空间包含多个带有I给定名字的,匹配类型参数数量的类型,那么命名空间或者类型名字是模糊的,并且将导致错误。
227
228\- 否则,命名空间或类型名字是未定义的,并且出现编译时错误。
229
230
231
232
233l 否则,命名空间或者类型名字是N.I或者N.I<a ,…,a="" 1="" n="">的形式,这里N是一个命名空间或类型名字,I是一个标识符,并且<a ,…,a="" 1="" n="">是一个可选类型实参列表。N 被作为命名空间或类型名字而首先决策。如果N的决策失败,将会出现一个编译时错误。否则N.I或者N.I<a ,…,a="" 1="" n="">将按如下方式决策。
234
235\- 如果N引用一个命名空间,并且如果I是内嵌在N中的命名空间名字,并且没有指定类型实参列表,那么命名空间或类型名字引用该内嵌命名空间。
236
237\- 否则,如果N引用一个命名空间,并且I是在带有匹配类型参数数量的N中的可访问类型的名字,那么命名空间或类型名字引用由给定类型实参构造的那个类型。
238
239\- 否则,如果N引用类或结构类型,并且I是内嵌在带有与类型参数匹配的N中的可访问类型的名字,那么命名空间或者类型名字引用由给定实参构造的那个类型。
240
241\- 否则,N.I是一个无效的命名空间名字,并且会出现编译时错误。
242
243### 20.9.2成员查找
244
245下面内容可替换§7.3
246
247成员查找是一种根据在上下文中的意义来确定类型的处理过程。在一个表达式中,成员查找可以作为计算一个简单名字或成员访问(§20.9.4)而出现。
248
249在类型T中的名字N的成员查找按如下规则确定。
250
251 * 首先一组名为N的可访问成员被确定。
252
253
254
255\- 如果T是一个类型参数,那么在每个作为T的类约束或接口约束而指定的类型,连同在 object 中的N的命名成员的集合中,这个集合就是命名的可访问成员的联合。
256
257\- 否则,这个集合由T中的N的所有命名可访问成员组成,包括继承成员和在 object 中的N的命名可访问成员。如果T 是一个构造类型,成员的集合通过替换§20.5.4中描述的类型实参而得到。包括 override 修饰符的成员将从集合中排出。
258
259
260
261
262 * 接着,通过其他成员而被隐藏的成员将从这个集合中删除。对于在集合中的每个成员S.M,S是M在其中被声明的类型,下面的规则可适用
263
264
265
266\- 如果M是一个常数、字段、属性、事件或枚举成员,那么在S的所有基类中声明的成员都将从这个集合删除。
267
268\- 如果M是一个类型声明,那么在 S基类中的所有非类型声明都从集合被删除,并且,作为S 在基类型中声明的M的所有带有相同数量类型参数的类型声明,都将从该集合中删除。
269
270\- 如果M是一个方法,那么在S的基类中声明的所有非方法成员,都将从这个集合删除,并且,作为S 在基类型中声明的M的带有相同签名的所有方法都将从这个集合中删除。
271
272 * 接着,通过类成员隐藏的接口成员将从该集合中删除。这一步,只有当T是一个类型参数,并且T有类约束和多个接口约束时才有效。对于在集合中的每个成员 S.M ,其中S是M在其中声明的类型,如果S是一个类声明而不是 object 的话,下面的规则适用
273
274
275
276\- 如果M是一个常数、字段、属性、事件、枚举成员或类型声明,那么在接口声明中声明的所有成员都将从这个集合中删除。
277
278\- 如果M是一个方法,那么在接口类型中声明的所有非方法成员都将从这个集合中删除,并且,作为S 在接口中声明的M的带有相同签名的所有方法都将从这个集合中删除。
279
280 * 最后,在删除隐藏成员之后,查找的结果将被确定
281
282
283
284\- 如果由单一成员组成的集合,不是类型、方法,那么这个成员就是查找的结果。
285
286\- 否则,如果集合只包含方法,那么这组方法就是查找的结果。
287
288\- 否则,如果集合只包含类型声明,那么这组类型声明在成员查找的结果中。
289
290\- 否则,查找是不明确的,将会出现编译时错误。
291
292对于类型,而不是类型参数和接口的成员查找来说,在接口中的成员查找是严格的单继承(在继承链中的每个接口恰好有零个或一个直接基接口),查找规则的影响只是派生成员隐藏带有相同名字和签名的基类成员。这种单继承查找是很明确的。成员查找中可能引起的模糊性来自于§13.2.5中描述的多重继承接口
293
294**
295**
296
297### 20.9.3简单名字
298
299如下内容可替换§7.5.2。
300
301简单名字由一个标识符,其后可跟随可选的类型参数列表。
302
303_simple-name:_ (简单名字:)
304
305_identifier type-argument-list_ opt (标识符 类型实参列表 可选 )
306
307对于I或I<a ,…,a="" 1="" n="">形式的简单名字,这里I 是一个标识符,I<a ,…,a="" 1="" n="">是一个可选类型实参列表,可以被按如下方式计算和分类。
308
309 * 如果简单名字出现在块内,并且如果块的局部变量声明空间包含由I给定名字的局部变量或参数,那么,这个简单名字引用该局部变量和参数,并且作为一个变量而被分类。如果类型实参列表被指定,将会出现编译时错误。
310 * 如果简单名字出现在泛型方法声明体之内,并且如果该声明包含由I给定名字的类型参数,那么简单名字引用那个类型参数,如果只定了类型实参列表被,将会出现编译时错误。
311 * 否则,对于以直接封闭类、结构或枚举声明的实例类型开始的类型T的每个实例,继续采用每个封闭外部类或结构声明的实例类型(如果有的话)。
312
313
314
315\- 如果T的声明包括由 I给定名字的类型参数,那么,简单名字引用该类型参数。如果类型实参列表被指定,将会出现编译时错误。
316
317\- 否则,如果在T 中I的成员查找产生一个匹配
318
319u 如果T是直接封闭类或结构类型的实例类型, 并且查找标识一个或多个方法,结果将是一个带有与 this 表达式关联的方法组。如果类型实参列表被指定,它被用在一个泛型方法调用中(§20.6.3)。
320
321u 如果T是直接封闭类或结构类型的实例类型,如果查找标识一个实例成员,并且引用出现在一个实例构造函数、实例方法或一个实例访问器的块之内,其结果将和 this.I 形式的成员访问相似。如果类型实参被指定,将出现编译时错误。
322
323u 否则,结果与T.I或T.I<a ,…,a="" 1="" n="">形式的成员访问相似。在这种情况下,引用实例成员的简单名字将是一个编译时错误。
324
325
326
327
328 * 否则,对于带有每个简单名字出现在其中的命名空间的命名空间N,继续采用每个封闭命名空间(如果有的话),并以全局命名空间结束,下面的步骤将被计算,直到一个实体被定位。
329
330
331
332\- 如果I是在N 中的一个命名空间的名字,并且没有指定类型实参列表,那么简单名字将引用该命名空间。
333
334\- 否则,如果I是在带有匹配类型参数数量的N中的一个可访问类型的名字,那么简单类型引用由给定类型实参构造的那个类型。
335
336u 如果命名空间声明包含一个关联由I给定名字的 using 别名指令,这里I是一个导入命名空间或类型,并且没有指定类型实参列表,那么简单名字引用该命名空间或类型。
337
338u 否则,如果由命名空间声明的 using 命名空间指令导入的命名空间,恰好包含一个由I给定名字的,匹配类型参数数量的类型,那么简单名字引用由给定类型实参构造的类型。
339
340u 否则,如果由命名空间声明的 using 命名空间指令导入的命名空间,包含多个由I给定名字,匹配类型参数数量的类型,那么简单名字是不明确的,将导致编译时错误
341
342l 否则,由简单名字给定的名字是未定义的,将导致编译时错误。
343
344### 20.9.4成员访问
345
346如下内容可代替§7.5.4。
347
348成员访问由基本表达式或预定义类型,紧接一个“.”标记,再紧接一个标识符,然后接着可选的类型实参列表而组成。
349
350_member-access:_ (成员访问:)
351
352_primary-expression . identifier type-argument-list_ opt (基本表达式. 标识符 类型实参列表 可选 )
353
354_predefined-type . identifier type-argument-list_ opt (预定义类型. 标识符 类型实参列表 可选 )预定义类型:下列之一
355
356bool byte char decimal double float int long
357
358object sbyte short string uint ulong ushort
359
360
361
362
363对于E.I或E.I<a ,…,a="" 1="" n="">形式的成员访问,这里E是一个基本表达式或预定义类型,I是一个标识符,并且<a ,…,a="" 1="" n="">是一个可选类型实参列表,将按如下方式被计算和分类。
364
365 * 如果E 是一个命名空间,I是E 中嵌套命名空间的名字,并且没有指定类型实参,那么结果就是这个命名空间。
366 * 如果E是一个命名空间,I是在E中可访问类型的名字,E 匹配类型参数的数量,那么结果就是由给定类型实参构造的类型。
367 * 如果E是一个预定义类型或作为一个类型而被分类的基本表达式,如果E 不是类型参数,并且如果在E中I的成员查找产生一个匹配,那么E.I被计算,并按如下分类。
368
369
370
371\- 如果I标识一个或多个类型声明,那么使用相同数量的(可能是零)类型参数来确定该类型声明,就像是在类型实参中提供的那样。结果就是由给定类型实参而构造的类型。如果类型声明不匹配类型参数的数量,将出现编译时错误。
372
373\- 如果I标识一个或多个方法,那么结果是没有关联实例表达式的方法组。如果类型实参列表被指定,它将在泛型方法调用中被使用(§20.6.3)。
374
375\- 如果I 标识一个静态属性、静态字段、静态事件、常数或一个枚举成员,并且如果类型实参列表被指定,将出现编译时错误。
376
377\- 如果I标识一个静态属性,那么结果是带有无关联实例表达式的属性访问。
378
379\- 如果I标识一个静态字段
380
381u 如果字段是只读的,并且引用发生在类或结构的静态构造函数之外,字段将在这里被声明。那么结果是一个值,也就是在E中静态字段I的值。
382
383u 否则,结果是一个变量,也就是在E中的静态字段I。
384
385\- 如果I标识一个静态事件
386
387u 如果引用发生在事件被声明的类或者结构中,并且事件被声明时没有使用事件访问器声明( event-accessor-declaration )(§10.7),那么E.I就好像I是一个静态字段一样被处理。
388
389u 否则,结果是一个无关联的实例表达式的事件访问。
390
391\- 如果I标识一个常数,那么结果是值,也就是常数的值。
392
393
394
395
396\- 如果I标识一个枚举成员,那么结果是一个值,也就是枚举成员的值。
397
398\- 否则,E.I是一个无效成员引用,并将导致编译时错误。
399
400 * 如果E是一个属性访问、索引器访问、变量或值,其类型是T,并且在T中I的成员查找产生一个匹配,那么E.I按如下方式被计算和分类。
401
402
403
404\- 首先,如果E是一个属性或索引器访问,那么属性或索引器访问的值将被获得(§7.1.1),并且E被重分类为值。
405
406\- 如果I标识一个或多个方法,那么结果是一个带有关联的E的实例表达式的方法组。如果类型实参列表被指定,它将在泛型方法调用中被使用(§20.6.3)。
407
408\- 如果I标识一个实例属性、实例字段或实例事件,并且如果类型实参列表被指定,将产生编译时错误。
409
410\- 如果I标识一个实例属性,那么结果是一个带有关联的E 的实例表达式。
411
412\- 如果T是一个类类型并且I 标识一个类类型的实例字段
413
414u 如果E的值是null,那么将会抛出 System.NullReferenceException 。
415
416u 否则,如果该字段是只读的,并且引用出现在字段声明的类的实例构造函数之外,那么结果是值,也就是由E引用的对象中 I的值。
417
418u 否则,结果是变量,也就是由E引用的对象中字段I。
419
420\- 如果T是一个结构类型,并且I 标识该结构类型的实例字段
421
422u 如果E是一个值,或者如果字段是只读的,并且引用出现在字段声明的结构的实例构造函数之外,那么结果是一个值,也就是由E给定的结构实例中字段I的值。
423
424u 否则,结果是一个变量,也就是由E给定结构实例中的字段I;
425
426\- 如果I标识一个实例事件
427
428u 如果引用出现在该事件被声明的类或结构之内,并且事件被声明时没有使用事件访问器声明,那么E.I就好像I是一个实例字段一样被处理。
429
430u 否则,结果是一个带有关联的E的实例表达式。
431
432 * 否则,E.I是一个无效成员引用,将导致编译时错误。
433
434
435
436**
437**
438
439### 20.9.5方法调用
440
441如下内容可替换§7.5.5.1中描述方法调用的编译时处理部分。
442
443对于M(A)形式的方法调用的编译时处理,其中M是一个方法组(可能包含一个类型实参列表),A是可选实参列表,由如下步骤组成。
444
445 * 方法调用的候选集合被构造。对于每个与方法组M关联的方法F
446
447
448
449\- 如果F是非泛型的,当如下成立时F是候选项
450
451u M没有类型实参列表,并且
452
453u 对于A(§7.4.2.1) ,F是适用的。
454
455\- 如果F是泛型,并且M没有类型实参列表,当如下成立时,F是候选项
456
457u 类型推断成功(§20.6.4),对于调用推断出类型实参的列表,并且
458
459u 一旦推断的类型实参替换了对应方法类型参数,F的参数列表对于A是适用的,并且
460
461u 在替换类型实参后,F 的参数列表,与适用的可能以其扩展形式(§7.4.2.1)在相同的类型中作为F而声明的非泛型方法是不同的。
462
463\- 如果F是泛型,并且M包括一个类型实参列表,当如下成立时,F是候选项
464
465u F和在类型实参列表中提供的一样,具有相同数量的方法类型参数,并且
466
467u 一旦,类型实参替换对应的方法类型参数,F的参数列表对于A 是可适用的(§7.4.2.1)。
468
469 * 候选方法的集合被缩减到只包含从深度派生类型而来的方法:对于在集合中的每个C.F方法,C是F在其中声明的类型,在C的基类型中声明的所有方法都被从集合中删除。
470 * 如果候选方法的结果集合是空的,那么没有可适用的方法存在,并且会出现编译时错误。如果候选方法并不是在同一类型中声明的,方法调用将是不明确的,并且会出现编译时错误(这后一种情况,只可能出现在对于一个在具有多重直接基接口的接口中的方法的调用,如§13.2.5的描述)。
471
472
473
474
475
476
477 * 候选方法集合的最佳方法使用重载决策规则(§7.4.2)标识。如果一个单一的最佳方法不能被标识,该方法调用是不明确的,并产生编译时错误。当执行重载决策时,泛型方法的参数在将对应的方法类型参数替换为类型实参(提供的或推断的)之后将被考虑。
478 * 被选择的最佳方法的最后验证被执行
479
480
481
482\- 方法在方法组的上下文中是有效的:如果方法是一个静态方法,方法组必须通过类型源自于简单名字或成员访问。如果该最佳方法是一个实例方法,方法组必须通过一个变量或值或者基类访问(base-access)源自于简单名字或成员访问。如果这些需求都不满足,那么将会出现编译时错误。
483
484\- 如果该最佳方法是一个泛型方法,类型实参(提供的或推断的)将被针对声明在泛型方法之上的约束作出检查。如果任何类型实参不满足对应类型参数的约束,将产生一个编译时错误。
485
486一旦方法根据前面的步骤被选择和验证,实际的运行时调用将根据§7.4.中的函数成员调用规则而被处理。
487
488### 20.9.6委托创建表达式
489
490如下内容可替换§7.5.10.3中委托创建表达式的编译时处理部分。
491
492对于 new D(E) 形式的委托创建表达式的编译时处理,其中D 是一个委托类型,E是一个表达式,由如下步骤组成。
493
494 * 如果E是一个方法组
495
496
497
498\- 对应于E(A)形式的方法调用,一个单一的方法调用将被选择。
499
500u D的参数类型和修饰符(ref或out)被用作实参类型和实参列表A的修饰符。
501
502u 在适用的测试和类型推断中,转换不予考虑。在隐式转换足够的实例中,类型要求是同一的。
503
504u 重载决策步骤不会执行。相反,候选的集合必须恰好包含一个与D兼容的方法(接着使用类型实参替换类型参数),并且这个方法将变成新创建委托所引用的方法。如果没有匹配的方法存在,或有多个匹配的方法存在,将发生编译时错误。
505
506
507
508
509\- 如果被选择的方法是一个实例方法,与E关联的实例表达式确定委托的目标对象。
510
511\- 结果是一个D类型的值,也就是引用选择的方法和目标对象的新创建委托。
512
513 * 否则,E是一个委托类型的值
514
515
516
517\- D和E必须兼容;否则出现编译时错误。
518
519\- 结果是D类型的值,也就是像E一样引用相同的调用列表的新创建的委托。
520
521 * 否则,委托创建表达式是无效的,并且出现编译时错误。
522
523
524
525## 20.10 右移语法改变
526
527泛型使用“<”和“>”字符分隔类型参数和类型实参(与C++的模板语法相似)。构造类型有时可嵌套,如 List<nullable<int>> ,但使用这种构件有一些微妙的语法问题:词法将组合这个构件的最后两个标记“>>”(右移运算符),而不是产生句法需要的两个“>”标记。尽管一个可能的解决方案是在两个“>>”中放入空格,这也是很尴尬而令人迷惑的,并没有以任何方式增加程序的简洁性。
528
529为了让这些中性的构件维持简单的词法,“>>”和“>>=”标记从词法中删除了,取代的是右移和右移赋值产生式。
530
531运算符或标点:之一
532
533{ } [ ] ( ) . , : ;
534
535\+ - * / % & | ^ ! ~
536
537= < > ? ++ -- && || == ->
538
539!= <= >= += -= *= /= %= &= |=
540
541^= << <<=
542
543right-shift:(右移:)
544
545> >
546
547right-shift-assignment:(右移赋值)
548
549> >=
550
551不像在句法中的其他产生式,在右移和右移赋值产生式的标记之间不允许任何种类的字符存在(甚至是空格)。
552
553
554
555
556下面的产生式被使用右移或右移赋值进行了修改。
557
558shift-expression: (移位表达式:)
559
560additive-expression (附加表达式)
561
562shift-expression << additive-expression (移位表达式 << 附加表达式)
563
564shift-expression right-shift additive-expression (移位表达式 right-shift 附加表达式)
565
566assignment-operator: (赋值运算符:)
567
568=
569
570+=
571
572-=
573
574*=
575
576/=
577
578%=
579
580&=
581
582|=
583
584^=
585
586<<=
587
588right-shift-assignment
589
590overloadable-binary-operator: (可重载二元运算符:)
591
592\+
593
594\-
595
596*
597
598/
599
600%
601
602&
603
604|
605
606^
607
608<<
609
610right-shift
611
612==
613
614!=
615
616>
617
618<
619
620>=
621
622<=
623
624(泛型完)
625
626****************************
627
628终于把这一章贴完了,哎,手都酸了,有问题的地方希望大家多多提出来,千万别客气:)</nullable<int></a></a></a></a></a></a></a></a></a></a></a></a></t></t></t></t></t></t></t></x<sytem.int32></int></x<t></t>