C#2.0 Specification(泛型二)

接(泛型一)

这篇文章是翻译的微软的技术文章.供学习c#的朋友参考,请勿用于商业目的。 http://msdn.microsoft.com/vcsharp/team/language/default.aspx

20.1.6 泛型类中的静态构造函数

在泛型类中的静态构造函数被用于初始化静态字段,为每个从特定泛型类声明中创建的不同的封闭构造类型,执行其他初始化。泛型类型声明的类型参数在作用域之内,可以在静态构造函数体内被使用。

如果下列情形之一发生,一个新的封闭构造类类型将被首次初始化。

  • 一个封闭构造类型的实例被创建时
  • 封闭构造类型的任何静态成员被引用时

为了初始化一个新的封闭的构造类类型,首先那个特定封闭类型的一组新静态字段(§ 20.1.5)将会被创建。每个静态字段都被初始化为其默认值(§5.2)。接着,静态字段初始化器(§10.4.5.1)将为这些静态字段执行。最后静态构造函数将被执行。

由于静态构造函数将为每个封闭构造类类型执行一次,那么在不能通过约束(§ 20.7)检查的类型参数上实施运行时检查,将会很方便。例如,下面的类型使用一个静态构造函数检查一个类型参数是否是一个引用类型。

class Gen

  1<t>
  2
  3{ 
  4
  5static Gen(){ 
  6
  7if((object)T.default != null){ 
  8
  9throw new ArgumentException(“T must be a reference type”); 
 10
 11} 
 12
 13} 
 14
 15} 
 16
 17##  20.1.7  访问受保护的成员 
 18
 19在一个泛型类声明中,对于继承的受保护的实例成员的访问是允许的,通过从泛型类构造的任何类型的实例就可以做到。尤其是,用于访问§  3.5.3中指定的protected和protected internal实例成员的规则,对于泛型使用如下的规则进行了扩充。 
 20
 21  * 在一个泛型类  G  中,对于一个继承的受保护的实例成员  M  ,使用  E.M  的基本表达式是允许的,前提是  E  的类型是一个从  G  构造的类类型,或继承于一个从  G  构造的类类型的类类型。 
 22
 23
 24
 25  
 26
 27
 28在例子 
 29
 30class C<t>
 31
 32{ 
 33
 34protected T x; 
 35
 36} 
 37
 38class D<t> :C<t>
 39
 40{ 
 41
 42static void F(){ 
 43
 44D<t> dt = new D<t>(); 
 45
 46D<int> di = new D<int>(); 
 47
 48D<string> ds = new D<string>(); 
 49
 50dt.x = T.default; 
 51
 52di.x = 123; 
 53
 54ds.x = “test”; 
 55
 56} 
 57
 58} 
 59
 60三个对  x的赋值语句都是允许的,因为它们都通过从泛型构造的类类型的实例发生。 
 61
 62###  20.1.8  在泛型类中重载 
 63
 64在一个泛型类声明中的方法、构造函数、索引器和运算符可以被重载。但为了避免在构造类中的歧义,这些重载是受约束的。在同一个泛型类声明中使用相同的名字声明的两个函数成员必须具有这样的参数类型,也就是封闭构造类型中不能出现两个成员使用相同的名字和签名。当考虑所有可能的封闭构造类型时,这条规则包含了在当前程序中目前不存在的类型是实参,但它仍然是可能出现的  [1]  。在类型参数上的类型约束由于这条规则的目的而被忽略了。 
 65
 66下面的例子根据这条规则展示了有效和无效的重载。 
 67
 68nterface I1<t> {…} 
 69
 70interface I2<t>{…} 
 71
 72class G1<u>
 73
 74{ 
 75
 76long F1(U u);  //  无效重载,  G<int> 将会有使用相同签名的两个成员 
 77
 78int F1(int i); 
 79
 80void F2(U u1, U u2);  //  有效重载,对于  U  没有类型参数 
 81
 82void F2(int I , string s);  //  可能同时是  int  和  string 
 83
 84void F3(I1<u>a);  //  有效重载 
 85
 86void F3(I2<u>a); 
 87
 88void F4(U a);  //  有效重载 
 89
 90void F4(U[] a);} 
 91
 92  
 93
 94
 95class G2<u,v>
 96
 97{ 
 98
 99void F5(U u , V v);  //  无效重载,  G2<int ,="" int=""> 将会有两个签名相同的成员 
100
101void F5(V v, U u); 
102
103void F6(U u , I1<v> v);//  无效重载,  G2<i1<int>,int&gt; 将会有两个签名相同的成员 
104
105void F6(I1<v> v , U u); 
106
107void F7(U u1,I1<v> V2);//  有效的重载,  U  不可能同时是  V  和  I1<v>
108
109void F7(V v1 , U u2); 
110
111void F8(ref U u);  //  无效重载 
112
113void F8(out V v); 
114
115} 
116
117class C1{…} 
118
119class C2{…} 
120
121class G3<u ,="" v=""> where U:C1 where V:C2 
122
123{ 
124
125void F9(U u);  //  无效重载,当检查重载时,在  U  和  V  上的约束将被忽略 
126
127void F9(V v); 
128
129} 
130
131###  20.1.9  参数数组方法和类型参数 
132
133类型参数可以被用在参数数组的类型中。例如,给定声明 
134
135class C<v>
136
137{ 
138
139static void F(int x, int y ,params V[] args);   
140} 
141
142方法的扩展形式的如下调用 
143
144C<int>.F(10, 20); 
145
146C<object>.F(10,20,30,40); 
147
148C<string>.F(10,20,”hello”,”goodbye”); 
149
150对应于如下形式: 
151
152C<int>.F(10,20, new int[]{}); 
153
154C<object>.F(10,20,new object[]{30,40}); 
155
156C<string>.F(10,20,new string[](“hello”,”goodbye”)); 
157
158###  20.1.10  重写和泛型类 
159
160在泛型类中的函数成员可以重写基类中的函数成员。如果基类是一个非泛型类型或封闭构造类型,那么任何重写函数成员不能有包含类型参数的组成类型。然而,如果一个基类是一个开放构造类型,那么重写函数成员可以使用在其声明中的类型参数。当重写基类成员时,基类成员必须通过替换类型实参而被确定,如§  20.5.4中所描述的。一旦基类的成员被确定,用于重写的规则和非泛型类是一样的。 
161
162  
163
164
165下面的例子演示了对于现有的泛型其重写规则是如何工作的。 
166
167abstract class C<t>
168
169{ 
170
171public virtual T F(){…} 
172
173public virtual C<t> G(){…} 
174
175public virtual void H(C<t> x ){…} 
176
177} 
178
179class D:C<string>
180
181{ 
182
183public override string F(){…}//OK 
184
185public override C<string> G(){…}//OK 
186
187public override void H(C<t> x); //  错误,应该是  C<string>
188
189} 
190
191class E<t,u>:C<u>
192
193{ 
194
195public override U F(){…}//OK 
196
197public override C<u> G(){…}//OK 
198
199public override void H(C<t> x){…}//  错误,应该是  C<u>
200
201} 
202
203###  20.1.11  泛型类中的运算符 
204
205泛型类声明可以定义运算符,它遵循和常规类相同的规则。类声明的实例类型(§  20.1.2)必须以一种类似于运算符的常规规则的方式,在运算符声明中被使用,如下 
206
207  * 一元运算符必须接受一个实例类型的单一参数。一元运算符“  ++  ”和“  —  ”必须返回实例类型。 
208  * 至少二元运算符的参数之一必须是实例类型。 
209  * 转换运算符的参数类型和返回类型都必须是实例类型。 
210
211
212
213下面展示了在泛型类中几个有效的运算符声明的例子 
214
215class X<t>
216
217{ 
218
219public static X<t> operator ++(X(T) operand){…} 
220
221public static int operator *(X<t> op1, int op2){…} 
222
223public static explicit operator X<t>(T value){…} 
224
225} 
226
227  
228
229
230对于一个从源类型  S到目标类型T的转换运算符,当应用§10.9.3中的规则时,任何关联S或T的类型参数被认为是唯一类型,它们与其他类型没有继承关系,并且在这些类型参数上的任何约束都将被忽略。 
231
232在例子 
233
234class C<t>{…} 
235
236class D<t>:C<t>
237
238{ 
239
240public static implicit operator C<int>(D<t> value){…}//OK 
241
242public static implicit operator C<t>(D<t> value){…}//  错误 
243
244} 
245
246第一个运算符声明是允许的,由于§  10.9.3的原因,T和int被认为是没有关系的唯一类型。然而,第二个运算符是一个错误,因为C<t>是D<t>的基类。 
247
248给定先前的例子,为某些类型实参声明运算符,指定已经作为预定义转换而存在的转换是可能的。 
249
250struct Nullable<t>
251
252{ 
253
254public static implicit operator Nullable<t>(T value){…} 
255
256public static explicit operator T(Nullable<t> value){…} 
257
258} 
259
260当类型  object  作为  T的类型实参被指定,第二个运算符声明了一个已经存在的转换(从任何类型到  object  是一个隐式的,也可以是显式的转换)。 
261
262在两个类型之间存在预定义的转换的情形下,在这些类型上的任何用户定义的转换都将被忽略。尤其是 
263
264  * 如果存在从类型  S  到类型  T  的预定义的隐式转换(§  6.1  ),所有用户定义的转换(隐式的或显式的)都将被忽略。 
265  * 如果存在从类型  S  到类型  T  的预定义的显式转换,那么任何用户定义的从类型  S  到类型  T  的显式转换都将被忽略。但用户定义的从  S  到  T  的隐式转换仍会被考虑。 
266
267
268
269对于所有类型除了  object  ,由  Nullable<t> 类型声明的运算符都不会与预定义的转换冲突。例如 
270
271  
272
273
274void F(int I , Nullable<int> n){ 
275
276i = n;  //  错误 
277
278i = (int)n;  //  用户定义的显式转换 
279
280n = i;  //  用户定义的隐式转换 
281
282n = (Nullable<int>)i;  //  用户定义的隐式转换 
283
284} 
285
286然而,对于类型  object  ,预定义的转换在所有情况隐藏了用户定义转换  ,  除了一种情况: 
287
288void F(object o , Nullable<object> n){ 
289
290o = n;  //  预定义装箱转换 
291
292o= (object)n;  //  预定义装箱转换 
293
294n= o;  //  用户定义隐式转换 
295
296n = (Nullable<object>)o;  //  预定义取消装箱转换 
297
298} 
299
300###  20.1.12  泛型类中的嵌套类型 
301
302泛型类声明可以包含嵌套类型声明。封闭类的类型参数可以在嵌套类型中使用。嵌套类型声明可以包含附加的类型参数,它只适用于该嵌套类型。 
303
304包含在泛型类声明中的每个类型声明是隐式的泛型类型声明。当编写一个嵌套在泛型类型内的类型的引用时,包含构造类型,包括它的类型实参,必须被命名。然而,在外部类中,内部类型可以被无限制的使用;当构造一个内部类型时,外部类的实例类型可以被隐式地使用。下面的例子展示了三个不同的引用从  Inner创建的构造类型的方法,它们都是正确的;前两个是等价的。 
305
306class Outer<t>
307
308{ 
309
310class Inner<u>
311
312{ 
313
314static void F(T t , U u){…} 
315
316} 
317
318static void F(T t) 
319
320{ 
321
322Outer<t>.Inner<string>.F(t,”abc”);//  这两个语句有同样的效果 
323
324Inner<string>.F(t,”abc”); 
325
326Outer<int>.Inner<string>.F(3,”abc”);  //  这个类型是不同的 
327
328Outer.Inner<string>.F(t , “abc”);  //  错误,  Outer  需要类型参数 
329
330} 
331
332} 
333
334尽管这是一种不好的编程风格,但嵌套类型中的类型参数可以隐藏一个成员,或在外部类型中声明的一个类型参数。 
335
336  
337
338
339class Outer<t>
340
341{ 
342
343class Inner<t> //  有效,隐藏了  Ouer  的  T 
344
345{ 
346
347public T t; //  引用  Inner  的  T 
348
349} 
350
351} 
352
353###  20.1.13  应用程序入口点 
354
355应用程序入口点不能在一个泛型类声明中。 
356
357##  20.2  泛型结构声明 
358
359像类声明一样,结构声明可以有可选的类型参数。 
360
361_struct-declaration_ :  (结构声明:) 
362
363_attributes_ opt  _struct-modifiers_ opt  struct  _identifier_ _type-parameter-list_ opt  _struct-interfaces_ opt  _type-parameter-constraints-clauses_ opt  _struct-body_ ;  opt 
364
365(特性  可选  结构修饰符  可选  struct  标识符  类型参数列表  可选  结构接口  可选  类型参数约束语句  可选  结构体  ;  可选  ) 
366
367除了§  11.3中为结构声明而指出的差别之外,泛型类声明的规则也适用于泛型结构声明。 
368
369##  20.3  泛型接口声明 
370
371接口也可以定义可选的类型参数 
372
373_interface-declaration_ :  (接口声明:) 
374
375_attribute_ opt  _interface-modifiers_ opt  interface  _indentifier_ _type-parameter-list_ opt 
376
377_interface-base_ opt  _type-parameter-constraints-clause_ opt  _interface-body_ ; 
378
379(特性  可选  接口修饰符  可选  interface  标识符  类型参数列表  可选  基接口  可选  类型参数约束语句  可选  接口体;  可选  ) 
380
381使用类型参数声明的接口是一个泛型接口声明。除了所指出的那些,泛型接口声明遵循和常规结构声明相同的规则。 
382
383在接口声明中的每个类型参数在接口的声明空间定义了一个名字。在一个接口上的类型参数的作用域包括基接口、类型约束语句和接口体。在其作用域之内,一个类型参数可以被用作一个类型。应用到接口上的类型参数和应用到类(§  20.1.1)上的类型参数具有相同的限制。 
384
385在泛型接口中的方法与泛型类(§  20.1.8)中的方法遵循相同的重载规则。 
386
387**   
388**
389
390###  20.3.1  实现接口的唯一性 
391
392由泛型类型声明实现的接口必须为所有可能的构造类型保留唯一性。没有这条规则,将不可能为特定的构造类型确定调用正确的方法。例如,假定一个泛型类声明允许如下写法。 
393
394interface I<t>
395
396{ 
397
398void F(); 
399
400} 
401
402class X<u, v="">:I<u>,I<v> //  错误  ,I<u> 和  I<v> 冲突 
403
404{ 
405
406void I<u>.F(){…} 
407
408void I<v>.F(){…} 
409
410} 
411
412如果允许这么写,那么下面的情形将无法确定执行那段代码。 
413
414I<int> x = new X<int ,int="">(); 
415
416x.F(); 
417
418为了确定一个泛型类型声明的接口列表是有效的,可以按下面的步骤进行。 
419
420  * 让  L  成为在泛型类、结构或接口声明  C  中指定的接口的列表。 
421  * 将任何已经在  L  中的接口的基接口添加到  L 
422  * 从  L  中删除任何重复的接口 
423  * 在类型实参被替换到  L  后,如果任何从  C  创建的可能构造类型,导致在  L  中的两个接口是同一的,那么  C  的声明是无效的。当确定所有可能的构造类型时,约束声明不予考虑。 
424
425
426
427在类声明  X之上,接口列表L由I<u>和I<v>组成。该声明是无效的,因为任何使用相同类型U和V的构造类型,将导致这两个接口是同一的。 
428
429###  20.3.2  显式接口成员实现 
430
431使用构造接口类型的显式接口成员实现本质上与简单接口类型方式上是相同的。和以往一样,显式接口成员</v></u></int></int></v></u></v></u></v></u></u,></t></t></t></string></string></int></string></string></t></u></t></object></object></int></int></t></t></t></t></t></t></t></t></t></int></t></t></t></t></t></t></t></u></t></u></u></t,u></string></t></string></string></t></t></t></string></object></int></string></object></int></v></u></v></v></v></i1<int></v></int></u,v></u></u></int></u></t></t></string></string></int></int></t></t></t></t></t></t>
Published At
Categories with Web编程
Tagged with
comments powered by Disqus