[翻译]C#中的泛型 (From dotNet SDK 2.0 Beta1)

** 来源: Mircrosoft.NET 2.0 Beta1 SDK **

** 日期: 2004-11-2 **

泛型( generic )是 C# 语言 2.0 和通用语言运行时( CLR )的一个新特性。泛型为 .NET 框架引入了类型参数( type parameters )的概念。类型参数使得设计类和方法时,不必确定一个或多个具体参数,其的具体参数可延迟到客户代码中声明、实现。这意味着使用泛型的类型参数 T ,写一个类 MyList

  1<t> ,客户代码可以这样调用:  MyList<int> ,  MyList<string> 或  MyList<myclass> 。这避免了运行时类型转换或装箱操作的代价和风险。 
  2
  3** 目录  **
  4
  5**C#** ** 中的泛型  ** .  1 
  6
  7** 一、泛型概述  ** .  2 
  8
  9** 二、泛型的优点  ** .  5 
 10
 11** 三、泛型类型参数  ** .  7 
 12
 13** 四、类型参数的约束  ** .  8 
 14
 15** 五、泛型类  ** .  11 
 16
 17** 六、泛型接口  ** .  13 
 18
 19** 七、泛型方法  ** .  19 
 20
 21** 八、泛型委托  ** .  21 
 22
 23** 九、泛  型代码中的default  ** ** 关键字  ** .  23 
 24
 25** 十、C++  ** ** 模板和C#  ** ** 泛型的区别  ** .  24 
 26
 27** 十一  ** ** 、运行时中的泛型  ** .  25 
 28
 29** 十二  ** ** 、基础类库中的泛型  ** .  27 
 30
 31**   
 32**
 33
 34** 一、泛型概述  ** ** **
 35
 36泛型类和泛型方法兼复用性、类型安全和高效率于一身,是与之对应的非泛型的类和方法所不及。泛型广泛用于容器(  collections  )和对容器操作的方法中。  .NET  框架  2.0  的类库提供一个新的命名空间  System.Collections.Generic  ,其中包含了一些新的基于泛型的容器类。要查找新的泛型容器类(  collection classes  )的示例代码,请参见基础类库中的泛型。当然,你也可以创建自己的泛型类和方法,以提供你自己的泛化的方案和设计模式,这是类型安全且高效的。下面的示例代码以一个简单的泛型链表类作为示范。  (多数情况下,推荐使用由  .NET  框架类库提供的  List<t> 类,而不是创建自己的表。  )类型参数  _ T  _ 在多处使用,  具体类型通常在  这些地方  来指明表中元素的类型。  类型参数  T  有以下几种用法: 
 37
 38l  在  AddHead  方法中,  作为方法参数的类型。 
 39
 40l  在公共  方法  GetNext  中,以及嵌套类  Node  的  Data  属性中作为返回值的类型。 
 41
 42l  在嵌套类中,作为私有成员  data  的类型。 
 43
 44注意一点,  T  对嵌套的类  Node  也是有效的。当用一个具体类来实现  MyList<t> 时——如  MyList<int> ——每个出现过的  T  都要用  int  代替。 
 45
 46using System; 
 47
 48using System.Collections.Generic; 
 49
 50public class MyList<t> //type parameter T in angle brackets 
 51
 52{ 
 53
 54private Node head; 
 55
 56// The nested type is also generic on T. 
 57
 58private class Node 
 59
 60{ 
 61
 62private Node next; 
 63
 64//T as private member data type: 
 65
 66private T data; 
 67
 68//T used in non-generic constructor: 
 69
 70public Node(T t) 
 71
 72{ 
 73
 74next = null; 
 75
 76data = t; 
 77
 78} 
 79
 80public Node Next 
 81
 82{ 
 83
 84get { return next; } 
 85
 86set { next = value; } 
 87
 88} 
 89
 90//T as return type of property: 
 91
 92public T Data 
 93
 94{ 
 95
 96get { return data; } 
 97
 98set { data = value; } 
 99
100} 
101
102} 
103
104public MyList() 
105
106{ 
107
108head = null; 
109
110} 
111
112//T as method parameter type: 
113
114public void AddHead(T t) 
115
116{ 
117
118Node n = new Node(t); 
119
120n.Next = head; 
121
122head = n; 
123
124} 
125
126public IEnumerator<t> GetEnumerator() 
127
128{ 
129
130Node current = head; 
131
132while (current != null) 
133
134{ 
135
136yield return current.Data; 
137
138current = current.Next; 
139
140} 
141
142} 
143
144} 
145
146下面的示例代码演示了客户代码如何使用泛型类  MyList<t> ,来创建一个整数表。通过简单地改变参数的类型,很容易改写下面的代码,以创建字符串或其他自定义类型的表。 
147
148class Program 
149
150{ 
151
152static void  Main  (string[] args) 
153
154{ 
155
156//int is the type argument. 
157
158MyList<int> list = new MyList<int>(); 
159
160for (int x = 0; x &lt; 10; x++) 
161
162list.AddHead(x); 
163
164foreach (int i in list) 
165
166{ 
167
168Console.WriteLine(i); 
169
170} 
171
172Console.WriteLine("Done"); 
173
174} 
175
176} 
177
178  
179
180
181** 二、泛型的优点  ** ** **
182
183针对早期版本的通用语言运行时和  C#  语言的局限,泛型提供了一个解决方案。以前类型的泛化(  generalization  )是靠类型与全局基类  System.Object  的相互转换来实现。  .NET  框架基础类库的  ArrayList  容器类,就是这种局限的一个例子。  ArrayList  是一个很方便的容器类,使用中无需更改就可以存储任何引用类型或值类型。 
184
185//The .NET Framework 1.1 way of creating a list 
186
187ArrayList list1 = new ArrayList(); 
188
189list1.Add(3); 
190
191list1.Add(105); 
192
193//... 
194
195ArrayList list2 = new ArrayList(); 
196
197list2.Add(“It is raining in  Redmond  .”); 
198
199list2.Add("It is snowing in the mountains."); 
200
201//... 
202
203但是这种便利是有代价的,这需要把任何一个加入  ArrayList  的引用类型或值类型都隐式地向上转换成  System.Object  。如果这些元素是值类型,那么当加入到列表中时,它们必须被装箱;当  重新取回  它们时,要拆箱。类型转换和装箱、拆箱的操作都降低了性能;在必须迭代(  iterate  )大容器的情况下,装箱和拆箱的影响可能十分显著。 
204
205另一个局限是缺乏编译时的类型检查,当一个  ArrayList  把任何类型都转换为  Object  ,就无法在编译时预防客户代码类似这样的操作: 
206
207ArrayList list = new ArrayList(); 
208
209//Okay. 
210
211list.Add(3); 
212
213//Okay, but did you really want to do this? 
214
215list.Add(.“It is raining in  Redmond  .”); 
216
217int t = 0; 
218
219//This causes an InvalidCastException to be returned. 
220
221foreach(int x in list) 
222
223{ 
224
225t += x; 
226
227} 
228
229虽然这样完全合法,并且有时是有意这样创建一个包含不同类型元素的容器,但是把  string  和  int  变量放在一个  ArrayList  中,几乎是在制造错误,而这个错误直到运行的时候才会被发现。 
230
231在  1.0  版和  1.1  版的  C#  语言中,你只有通过编写自己的特定类型容器,才能避免  .NET  框架类库的容器类中泛化代码(  generalized code  )的危险。当然,因为这样的类无法被其他的数据类型复用,也就失去泛型的优点,你必须为每个需要存储的类型重写该类。 
232
233ArrayList  和其他相似的类真正需要的是一种途径,能让客户代码在实例化之前指定所需的特定数据类型。这样就不需要向上类型转换为  Object  ,而且编译器可以同时进行类型检查。换句话说,  ArrayList  需要一个类型参数。这正是泛型所提供的。在  System.Collections.Generic  命名空间中的泛型  List<t> 容器里,同样是把元素加入容器的操作,类似这样: 
234
235The .NET Framework 2.0 way of creating a list 
236
237List<int> list1 = new List<int>(); 
238
239//No boxing, no casting: 
240
241list1.Add(3); 
242
243//Compile-time error: 
244
245list1.Add("It is raining in  Redmond  ."); 
246
247与  ArrayList  相比,  在客户代码中唯一增加的  List<t> 语法是声明和实例化中的类型参数。代码略微复杂的回报是,你创建的表不仅比  ArrayList  更安全,而且明显地更加快速,尤其当表中的元素是值类型的时候。 
248
249  
250
251
252** 三、泛型类型参数  ** ** **
253
254在泛型类型或泛型方法的定义中,类型参数是一个占位符(  placeholder  ),通常为一个大写字母,如  T  。在客户代码声明、实例化该类型的变量时,把  T  替换为客户代码所指定的数据类型。泛型类,如泛型概述中给出的  MyList<t> 类,不能用作  as-is  ,原因在于它不是一个真正的类型,而更像是一个类型的蓝图。要使用  MyList<t> ,客户代码必须在尖括号内指定一个类型参数,来声明并实例化一个已构造类型(  constructed type  )。这个特定类的类型参数可以是编译器识别的任何类型。可以创建任意数量的已构造类型实例,每个使用不同的类型参数,如下: 
255
256MyList<myclass> list1  = new MyList<myclass>(); 
257
258MyList<float> list2 = new MyList<float>(); 
259
260MyList<somestruct> list3 = new MyList<somestruct>(); 
261
262在这些  MyList<t> 的实例中,类中出现的每个  T  都将在运行的时候被类型参数所取代。依靠这样的替换,我们仅用定义类的代码,就创建了三个独立的类型安全且高效的对象。有关  CLR  执行替换的详细信息,请参见运行时中的泛型。 
263
264  
265
266
267** 四、类型参数的约束  ** ** **
268
269若要检查表中的一个元素,以确定它是否合法或是否可以与其他元素相比较,那么编译器必须保证:客户代码中可能出现的所有类型参数,都要支持所需调用的操作或方法。这种保证是通过在泛型类的定义中,应用一个或多个约束而得到的。一个约束类型是一种基类约束,它通知编译器,只有这个类型的对象或从这个类型派生的对象,可被用作类型参数。一旦编译器得到这样的保证,它就允许在泛型类中调用这个类型的方法。上下文关键字  where  用以实现约束。下面的示例代码说明了应用基类约束,为  MyList<t> 类增加功能。 
270
271public class Employee 
272
273{ 
274
275public class Employee 
276
277{ 
278
279private string name; 
280
281private int id; 
282
283public Employee(string s, int i) 
284
285{ 
286
287name = s; 
288
289id = i; 
290
291} 
292
293public string Name 
294
295{ 
296
297get { return name; } 
298
299set { name = value; } 
300
301} 
302
303public int ID 
304
305{ 
306
307get { return id; } 
308
309set { id = value; } 
310
311} 
312
313} 
314
315} 
316
317class MyList<t> where T: Employee 
318
319{ 
320
321//Rest of class as before. 
322
323public T FindFirstOccurrence(string s) 
324
325{ 
326
327T t = null; 
328
329Reset(); 
330
331while (HasItems()) 
332
333{ 
334
335if (current != null) 
336
337{ 
338
339//The constraint enables this: 
340
341if (current.Data.Name == s) 
342
343{ 
344
345t = current.Data; 
346
347break; 
348
349&lt;SPAN lan</t></t></t></somestruct></somestruct></float></float></myclass></myclass></t></t></t></int></int></t></int></int></t></t></t></int></t></t></myclass></string></int></t>
Published At
Categories with Web编程
Tagged with
comments powered by Disqus