接泛型二
这篇文章是翻译的微软的技术文章.供学习c#的朋友参考,请勿用于商业目的。 http://msdn.microsoft.com/vcsharp/team/language/default.aspx
20.4 泛型委托声明
委托声明可以包含类型参数。
delegate-declaration :
attributes opt delegate-modifiers op t delegate return-type identifier type-parameter-list opt
( formal-parameter-list opt ) type-parameter-constraints-clauses opt ;
(委托声明: 特性 可选 委托修饰符 可选 delegate 返回类型 标识符 类型参数列表 可选 (正式参数列表 可选 )类型参数约束语句 可选
使用类型参数声明的委托是一个泛型委托声明。委托声明只有在支持类型参数列表时,才能支持类型参数约束语句(§ 20.7)。除了所指出的之外,泛型委托声明和常规的委托声明遵循相同的规则。泛型委托声明中的每个类型参数,在与委托关联的特定声明空间(§3.3)定义了一个名字。在委托声明中的类型参数的作用域包括返回类型、正式参数列表和类型参数约束语句。
像其他泛型类型声明一样,必须给定类型实参以形成构造委托类型。构造委托类型的参数和返回值,由委托声明中构造委托类型的每个类型参数对应的实参替代所形成。而结果返回类型和参数类型用于确定什么方法与构造委托类型兼容。例如
delegate bool Predicate
1<t>(T value)
2
3class X
4
5{
6
7static bool F(int i){…}
8
9static bool G(string s){…}
10
11
12
13
14static void Main (){
15
16Predicate<int> p1 = F;
17
18Predicate<string> p2=G;
19
20}
21
22}
23
24注意在先前的 Main 方法中的两个赋值等价于下面的较长形式 .
25
26static void Main (){
27
28Predicate<int> p1 = new Predicate<int>(F);
29
30Predicate<string> p2 = new Predicate<string>(G);
31
32}
33
34由于方法组转换,较短的形式也是可以的,这在§ 21.9中有说明。
35
36## 20.5 构造类型
37
38泛型类型声明自身并不表示一个类型。相反,泛型类型声明通过应用类型实参的方式被用作形成许多不同类型的“蓝图”。类型参数被写在尖括号之间,并紧随泛型类型声明名字之后。使用至少一个实参而被命名的类型被称为 ** 构造类型( ** ** constructed type ** ** ) ** 。构造类型可以用在语言中类型名字可以出现的大多数地方。
39
40_type-name:_ (类型名字:)
41
42_namespace-or-type-name_ (命名空间或类型名字)
43
44_namespace-or-type-name:_ (命名空间或类型名字:)
45
46_identifier type-argument-list _ (标识符类型实参列表 可选 )
47
48_namespace-or-type-name. identifier_ _(_ 命名空间或类型名字 . 标识符)
49
50_type-argument-list opt_ (类型实参列表 可选 )
51
52构造类型也能被用在表达式中作为简单名字(§ 20.9.3)或者访问一个成员(§20.9.4)。
53
54当一个命名空间或类型名字被计算时,只有带有正确数量类型参数的泛型类型会被考虑。由此,只要类型有不同数量的类型参数并且声明在不同的命名空间,那么使用相同的标识符标识不同的类型是可能的。这对于在同一程序中混合使用泛型和非泛型类是很有用的。
55
56namespace System.Collections
57
58{
59
60class Queue{…}
61
62}
63
64
65
66
67namespace Sysetm.Collections.Generic
68
69{
70
71class Queue<elementtype>{…}
72
73}
74
75namespace MyApplication
76
77{
78
79using System.Collections;
80
81using System.Collections.Generic;
82
83class X
84
85{
86
87Queue q1; //System.Collections.Queue
88
89Queue<int> q2;//System.Collections.Generic.Queue
90
91}
92
93}
94
95在这些代码中对于名字查找的详细规则在§ 20.9中进行了描述。在这些代码中对于模糊性的决议在§20.6.5中进行了描述。
96
97类型名字可能标识一个构造类型,尽管它没有直接指定类型参数。这种情况在一个类型嵌套在一个泛型类声明中时就会出现,并且包含声明的实例类型将因为名字查找(§ 20.1.2)而被隐式地使用。
98
99class Outer<t>
100
101{
102
103public class Inner{…}
104
105public Inner i; //i 的类型是 Outer<t>.Inner
106
107}
108
109在不安全代码中,构造类型不能被用作非托管类型(§ 18.2)。
110
111### 20.5.1 类型实参
112
113在一个类型参数列表中的每个实参都只是一个类型。
114
115_type-argument-list:_ (类型实参列表:)
116
117_ <type-arguments> _ ( < 类型实参 > )
118
119_type-arguments:_ (类型实参:)
120
121_type-argument_ (类型实参)
122
123_type-arguments, type-argument_ (类型实参,类型实参)
124
125_type-argument:_ (类型实参:)
126
127_type_ (类型)
128
129类型实参反过来也可以是构造类型或类型参数。在不安全代码中(§ 18),类型实参不能是指针类型。每个类型实参必须遵循对应类型参数(§20.7.1)上的任何约束。
130
131**
132**
133
134### 20.5.2 开放和封闭类型
135
136所有类型都可以被分为 ** 开放类型( ** ** open type ** ** ) ** 或 ** 封闭类型 ** ** (closed type) ** 。开放类型是包含类型参数的类型。更明确的说法是
137
138 * 类型参数定义了一个开放类型
139 * 数组类型只有当其元素是一个开放类型时才是开放类型
140 * 构造类型只有当其类型实参中的一个或多个是开放类型时,它才是开放类型
141
142
143
144非开放类型都是封闭类型。
145
146在运行时,在泛型类型声明中的所有代码都在一个封闭构造类型的上下文执行,这个封闭构造类型是通过将类型实参应用到泛型声明中创建的。在泛型类型中的每个类型实参被绑定到一个特定运行时类型。所有语句和表达式的运行时处理总是针对封闭类型发生,而开放类型只发生在编译时处理。
147
148每个封闭构造类型都有它自己的一组静态变量,它们并不被其他封闭类型共享。因为在运行时不存在开放类型,所以开放类型没有关联的静态变量。如果两个封闭构造类型是从同一个类型声明构造的,并且对应的类型实参也是相同的类型,那么它们就是相同的类型。
149
150### 20.5.3 构造类型的基类和接口
151
152构造类类型有一个直接基类,就像是一个简单类类型。如果泛型类声明没有指定基类,其基类为 object 。如果基类在泛型类声明中被指定,构造类型的基类通过将在基类声明中的每个类型参数,替代为构造类型对应类型实参而得到。给定泛型类声明
153
154class B<u ,="" v="">{…}
155
156class G<t>:B<string ,="" t[]="">{…}
157
158构造类型 G<int> 的基类将会是 B<string ,="" int[]=""> 。
159
160相似地,构造类、结构和接口类型有一组显式的基接口。显式基接口通过接受泛型类型声明中的显式基接口声明和某种替代而形成,这种替代是将在基接口声明中的每个类型参数,替代为构造类型的对应类型实参。
161
162一个类型的所有基类和基接口通过递归地得到中间基类和接口的基类与接口而形成。例如,给定泛型类声明
163
164
165
166
167class A {…}
168
169class B<t>:A{…}
170
171class C<t>:B<icomparable<t>>{…}
172
173class D<t>:C<t[]>{…}
174
175D<int> 的基类是 C<int[]>,B<icomparable<int[]>> ,A和 object 。
176
177### 20.5.4 构造类型的成员
178
179构造类型的非继承成员通过替代成员声明的类型实参,构造类型的对应类型实参而得到。
180
181例如,给定泛型类声明
182
183class Gen<t,u>
184
185{
186
187public T[,],a;
188
189public void G(int i ,T t , Gen<u, t=""> gt){…}
190
191public U Prop(get{…}) set{…}}
192
193public int H{double d}{…}
194
195}
196
197构造类型 Gen<int[],icomparable<string>> 有如下的成员。
198
199public int[,][] a;
200
201public void G(int I , int[] t , Gen<icomparable<string>,int[] gt>){…}
202
203public IComparable<string> Prop{get{…} set{…}}
204
205public int H(double d){…}
206
207注意替代处理是基于类型声明的语义意义的,并不是简单的基于文本的替代。在泛型类声明 Gen中的成员a的类型是“T的二维数组” 因此在先前实例化类型中的成员a的类型是“int型的一维数组的二维数组”或int[,][]。
208
209构造类型的继承成员以一种相似的方法得到。首先直接基类的所有成员是已经确定的。如果基类自身是构造类型这可能包括当前规则的递归应用。然后,继承成员的每一个通过将成员声明中的每个类型参数,替代为构造类型对应类型实参而被转换。
210
211class B<u>
212
213{
214
215public U F(long index){…}
216
217}
218
219
220
221
222class D<t>:B<t[]>
223
224{
225
226public T G(string s){…}
227
228}
229
230在先前的例子中,构造类型 D<int> 的非继承成员 public int G(string s) 通过替代类型参数 T的类型实参 int 而得到。 D<int> 也有一个从类声明 B而来的继承成员。这个继承成员通过首先确定构造类型 B<t[]> 的成员而被确定, B<t[]> 成员的确定是通过将 U替换为替换为T[],产生 public T[] F(long index) 。然后类型实参 int 替换了类型参数 T,产生继承成员 public int[] F(long index) 。
231
232### 20.5.5 构造类型的可访问性
233
234当构造类型 C<t ,…,t="" 1="" n="">的所有部分C,T 1 ,…,T N 可访问时,那么它就是可访问的。例如,如果泛型类型名C是public,并且所有类型参数T 1 ,…,T N 也是 public ,那么构造类型的可访问性也是 public 。如果类型名或类型实参之一是 private ,那么构造类型的可访问性是 private 。如果类型实参之一可访问性是 protected ,另一个是 internal ,那么构造类型的可访问性仅限于该类,以及本程序集之内的子类。
235
236### 20.5.6 转换
237
238构造类型遵循与非泛型类型相同的规则(§ 6)。当应用这些规则时,构造类型的基类和接口必须按§20.5.3中所描述的方式确定。
239
240除了那些在§ 6中所描述的之外,构造引用类型之间不存在特别的转换。尤其是,不像数组类型,构造引用类型不允许“ co-variant ”转换。也就是说,类型 List<b> 不能转换到类型 List<a> (无论是隐式或显式)即使是 B派生于A也是如此。同样,也不存在从 List<b> 到 List<object> 的转换。
241
242对于这一点的基本原理是很简单的:如果可以转换到 List<a> ,很显然你可以存储一个类型 A的值到这个 list 中。这将破坏在 List<b> 类型中的每个对象总是类型 B的值这种不变性,或者当在集合类上赋值时,将出现不可预料的错误。
243
244
245
246
247转换的行为和运行时类型检查演示如下。
248
249class A {…}
250
251class B:A{…}
252
253class Colletion{…}
254
255class List<t>:Collection{…}
256
257class Test
258
259{
260
261void F()
262
263{
264
265List<a> listA = new List<a>();
266
267List<b> listB= new List<b>();
268
269Collection c1 = listA; //OK,List<a> 是一个集合
270
271Collection c2 = listB; //OK,List<b> 是一个集合
272
273List<a> a1 = listB; // 错误,没有隐式的转换
274
275List<a> a2 = (List<a>)listB; // 错误,没有显式的转换
276
277}
278
279}
280
281### 20.5.7System.Nullable<t> 类型
282
283在 .NET基类库中定义了泛型结构类型 System.Nullable<t> 泛型结构类型,它表示一个类型 T的值可以为 null 。 System.Nullable<t> 类型在很多情形下是很有用的,例如用于指示数据库表的可空列,或者 XML 元素中的可选特性。
284
285可以从一个 null 类型向任何由 System.Nullable<t> 类型构造的类型作隐式地转换。这种转换的结果就是 System.Nullable<t> 的默认值。也就是说,可以这样写
286
287Nullable<int> x = null;
288
289Nullable<string> y = null;
290
291和下面的写法相同。
292
293Nullable<int> x = Nullable<int>.default;
294
295Nullable<string> y = Nullable<string>.default;
296
297### 20.5.8 使用别名指令
298
299使用别名可以命名一个封闭构造类型,但不能命名一个没有提供类型实参的泛型类型声明。例如
300
301namespace N1
302
303{
304
305class A<t>
306
307{
308
309class B{}
310
311}
312
313
314
315
316class C{}
317
318}
319
320namespace N2
321
322{
323
324using W = N1.A; // 错误,不能命名泛型类型
325
326using X = N1.A.B; // 错误,不能命名泛型类型
327
328using Y = N1.A<int>; //ok, 可以命名封闭构造类型
329
330using Z = N1.C; //ok
331
332}
333
334### 20.5.9 特性
335
336开放类型不能被用于特性内的任何地方。一个封闭构造类型可以被用作特性的实参,但不能被用作特性名,因为 System.Attribute 不可能是泛型类声明的基类。
337
338class A:Attribute
339
340{
341
342public A(Type t){…}
343
344}
345
346class B<t>: Attribute{} // 错误,不能将 Attribute 用作基类
347
348class List<t>
349
350{
351
352[A(typeof(T))] T t; // 错误,在特性中有开放类型
353
354}
355
356class X
357
358{
359
360[A(typeof(List<int>))] int x; //ok, 封闭构造类型
361
362[B<int>] int y; // 错误,无效的特性名字
363
364}</int></int></t></t></int></t></string></string></int></int></string></int></t></t></t></t></t></a></a></a></b></a></b></b></a></a></t></b></a></object></b></a></b></t></t[]></t[]></int></int></t[]></t></u></string></icomparable<string></int[],icomparable<string></u,></t,u></icomparable<int[]></int[]></int></t[]></t></icomparable<t></t></t></string></int></string></t></u></type-arguments></t></t></int></elementtype></string></string></int></int></string></int></t>