泛型(Generic)是对CLR类型系统的扩展,用于定义未指定某些细节的类型。实际上,代码本身就是泛型。
使用泛型可以避免以下两个常见的问题:代码冗余和困扰开发人员的含混不清的编译器错误。假设集合类SortedList是Object引用的集合,GenericSortedList<T>是任意类型的集合,使用泛型具有以下明显的优点。
(1)类型安全
当用户向SortedList类型的集合内添加String时,String会隐式强制转换为Object。同样,如果从该列表中检索String对象,则它必须在运行时从Object引用强制转换到String引用。这样就会造成编译时缺少类型安全,从而使编写的代码容易出错。相反,如果使用GenericSortedList<String>(T的类型被设置为String),就会使所有的添加和查找方法是用String引用。这样在编译时就可以检查元素的类型是否正确。
(2)二进制代码重用
为了进行维护,开发人员可以选择使用SortedList,通过从它派生SortedListOfString来实现编译时的类型安全。此方法有一个问题,那就是必须对于每个需要类型安全列表的类型都编写新代码,而这会很快变成非常费力的工作。使用GenericSortedList<T>,需要执行的全部操作就是将具有所需元素类型的类型实例化为T。泛型代码还有一个附加价值,那就是它在运行时生成,因此,对于无关元素类型的两个扩展(如GenericSortedList<String>和GenericSortedList<FileStream>)能够重新使用同一个实时(JIT)编译代码的大部分。CLR只是处理细节就可以了,从而使代码不再臃肿。
(3)性能
如果能在编译时进行类型检查,而不是在运行时进行检查,则显然会大大增强系统的性能。在托管代码中,引用和值之间的强制转换既会导致装箱又会导致拆箱,而且避免这样的强制转换可能会对性能产生同样的负面影响。如果对一个由一百万个整数组成的数组进行快速排序法基准测试,就会发现泛型方法比非泛型方法快得多。这是由于完全避免了对这些值进行装箱。如果针对由字符串引用组成的数组进行同样的排序,则由于无需在运行时执行类型检查,因此使用泛型方法后大大提高了性能。
(4)清晰性
泛型的清晰性体现在许多方面。约束是泛型的一个功能,它会禁止对泛型代码进行不兼容的扩展;使用泛型,也不再有困扰C++模板用户的含混不清的编译器错误。在GenericSortedList<T>中,集合类将有一个约束,该约束使集合类只处理可进行比较并依此进行排序的T类型。同样,通常可以使用名为类型推理的功能来调用泛型的方法,而无需使用任何特殊语法。当然,编译时类型安全可以使应用程序代码更加清晰。
泛型的定义
CLR支持多种编程语言,因此,CLR泛型也有多种语法。但是,无论采用哪种语法,用一种面向CLR的语言编写的泛型代码也可以由其他面向CLR的语言编写的程序使用。
泛型的定义代码语法为:
[访问修饰符] [返回类型] 泛型支持类型泛型名称<类型参数列表>
其中,CLR支持泛型类、结构、方法、接口和委托等。泛型名称要符合标示符的定义。尖括号表示类型参数列表,尖括号紧跟在泛型类类型或成员的名称后面。同样,在类型参数列表中有一个或多个类型参数,形式如<T,U,…>。
例1:定义一个泛型类
1 class Node <T>
2 {
3 T data;
4 Node<T>next;
5 }
6
例2:定义一个泛型方法
1 void Swap<T>(ref T item1, ref T item2)
2 {
3 T temp=item1;
4 item1=item2;
5 item2=temp;
6 }
7
泛型的引用
引用泛型时,也可以将未指定的类型变成系统能够识别的指定的类型。例3和例4分别是对例1和例2的引用示例。
例3:引用一个泛型类
1 class Node8Bit:Node<Byte>
2 {
3 …
4 }
5
例4:引用一个泛型方法
1 Decimal d1=0,d2=2;
2 Swap<Decimal>(ref d1,ref d2);
通过例子可以看出,定义一个类或者方法时,可以利用泛型<T>代表任何一种类型,而在引用时再指定具体类型。在例6中,当代码调用泛型方法Swap<T>时,C#编译器会自动将定义的泛型转换为引用代码中指定的类型,从而大大简化了编程人员代码书写的工作量。
由于泛型<T>可以代表任何一种类型,因此只定义一次方法的参数类型就能实现所有类型的引用。例如,例6中的d1可能是int型、float型等(d2同样如此),如果不使用泛型,就需要写出很多重载的Swap方法,使代码既臃肿,又不易阅读,同时也增加了编译工作量。由此可以看出,泛型的优点是显而易见的。
常用的泛型集合
在.NET Framework类库中,System.Collections.Generic和System.Collections.ObjectModel命名空间中提供了很多泛型集合类。许多泛型集合类型是泛型类型的直接模拟。表1列出了常见的泛型集合类与非泛型集合类的对应关系。
表1 常见的泛型集合类及对应的非泛型集合类
1. List<T>
List泛型类表示可通过索引访问的对象的强类型列表,提供用于对列表进行搜索、排序和操作的方法。常用方法如下。
Add方法:将指定值的元素添加到List<>中。
Insert方法:在列表的中间插入一个新元素。
Contains方法:测试该列表中是否存在某个元素。
Remove方法:从列表中移除带有指定键的元素。
Clear方法:移除列表中的所有元素。
2. Dictionary<Tkey,Tvalue>
Dictionary泛型类提供了一组键到一组值的映射。字典中的每个添加项都由一个值及其相关联的键组成,通过键来检索。常用方法如下:
Add方法:将带有指定键和值的元素添加到Dictionary<,>中。
TryGetValue方法:获取与指定键相关联的值。
ContainsKey方法:确定Dictionary<,>中移除带有指定键的元素。
Remove方法:从Dictionary<,>移除带有指定键的元素。
3. Queue<T>
Queue泛型类表示对象的先进先出集合。
常用方法如下:
Enqueue方法:将指定元素插入列尾。
Dequeue方法:队列首元素出列。
4. Stack<T>
Stack泛型类表示同一任意类型的实例的大小可变的后进先出(LIFO)集合。
常用方法如下:
Push方法:将指定元素插入栈项。
Pop方法:将栈项元素弹出。
5. SortedList<Tkey,Tvalue>
SortedList泛型类表示键/值对的集合,这些键/值对基于关联的IComparer实现按照键进行排序。
常用方法如下:
Add方法:将带有指定键和值的元素添加到SortedList<,>中。
TryGetValue方法:获取与指定的键相关联的值。
ContainsKey方法:确定SortedList<,>中是否包含指定的键。
Remove方法:从SortedList<,>中移除带有指定键的元素。
例5:现有一个活期存款账户类Account,为其提供处理业务的Customers类有一个方法CreateAccount(账户名,开户金额),试使用SortedList<,>创建泛型对象,并判断是否存在账户“张三”,若不存在,则创建账户;若存在,则为其追加存款。
主要代码如下所示:
1 using System;
2 using System.IO;
3 using System.Collections.Generic;
4 namespace GenricExample
5 {
6 public class Account
7 {
8 private string accountName;
9 private int accountvalue;
10 public string AccountName
11 {
12 get
13 {
14 return accountName;
15 }
16 set
17 {
18 accountName=value;
19 }
20 }
21 public int AccountValue
22 {
23 get
24 {
25 return accountvalue;
26 }
27 set
28 {
29 accountvalue=value;
30 }
31 }
32 }
33 public class Customers
34 {
35 public void CreateAccount(string name,int value1)
36 {
37 Account acc=new Account();
38 acc.AccountName=name;
39 acc.AccountValue=value1;
40 }
41 }
42 class Porgram
43 {
44 static void Main(string[] args)
45 {
46 SortedList<string,Account> accounts=new SortedList<string,Account>();
47 Customers customs=new Customers();
48 if(accounts.ContainsKey("aa")==false)
49 {
50 //如果无此账户,创建账户,并将存款作为开户金额
51 Account account=new Account();
52 customs.CreateAccount("张三",1000);
53 accounts.Add("张三",account);
54 }
55 else
56 {
57 //如果有此账户,在现有账户上追加存款
58 Account account=accounts["张三"];
59 //account追加存款操作
60 }
61 }
62 }
63 }
64
作者:zeke
出处:http://zhf.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。