机制:C# 泛型类型替换是在运行时执行的,从而为实例化的对象保留了泛型类型信息。C#泛型代码在被编译为IL代码和无数据时,采用特殊的占位符来表示泛型类型,并用专有的IL指令支持泛型操作。而真正的泛型实例化工作以"on-demand"的方式,发生在JIT编译时。
举例:泛型通常用与集合以及作用于集合的方法一起使用。.NET Framework 2.0 版类库提供一个新的命名空间
当然,也可以创建自定义泛型类型和方法,以提供自己的通用解决方案,设计类型安全的高效模式。下面的代码示例演示一个用于演示用途的简单泛型链接列表类在通常使用具体类型来指示列表中存储的项的类型的场合,可使用类型参数 T。其使用方法如下:
1、在 AddHead 方法中作为方法参数的类型。
2、在 Node 嵌套类中作为公共方法 GetNext 和 Data 属性的返回类型。
3、在嵌套类中作为私有成员数据的类型。
注意,T 可用于 Node 嵌套类。如果使用具体类型实例化 GenericList<T>(例如,作为 GenericList<int>),则所有的 T 都将被替换为 int。
1
// 参数类型T2
public class GenericList<T> 3
{4
// The nested class is also generic on T.5
private class Node6
{7
// T used in non-generic constructor.8
public Node(T t)9
{10
next = null;11
data = t;12
}13

14
private Node next;15
public Node Next16
{17
get { return next; }18
set { next = value; }19
}20

21
// T as private member data type.22
private T data;23

24
// T as return type of property.25
public T Data 26
{27
get { return data; }28
set { data = value; }29
}30
}31

32
private Node head;33
public GenericList() 34
{35
head = null;36
}37

38
// 用T做为方法的参数类型39
public void AddHead(T t) 40
{41
Node n = new Node(t);42
n.Next = head;43
head = n;44
}45

46
public IEnumerator<T> GetEnumerator()47
{48
Node current = head;49

50
while (current != null)51
{52
yield return current.Data;53
current = current.Next;54
}55
}56
}57

1
class TestGenericList2
{3
static void Main()4
{5
// int is the type argument6
GenericList<int> list = new GenericList<int>();7

8
for (int x = 0; x < 10; x++)9
{10
list.AddHead(x);11
}12

13
foreach (int i in list)14
{15
System.Console.Write(i + " ");16
}17
System.Console.WriteLine("\nDone");18
}19
}20

C#泛型编译机制:
第一轮编译时,编译器只为Stack<T>(栈算法)类型产生“泛型版”的IL代码与元数据-----并不进行泛型类型的实例化,T在中间只充当占位符
JIT编译时,当JIT编译器第一次遇到Stack<int>时,将用int替换“泛型版”IL代码与元数据中的T---进行泛型类型的实例化。
CLR为所有类型参数为“引用类型”的泛型类型产生同一份代码;但如果类型参数为“值类型”,对每一个不同的“值类型”,CLR将为其产生一份独立的代码。
在公共语言运行库和 C# 语言的早期版本中,通用化是通过在类型与通用基类型
使用非泛型集合类的限制可以通过编写一小段程序来演示,该程序利用 .NET Framework 类库中的
1
System.Collections.ArrayList list1 = new System.Collections.ArrayList();2
list1.Add(3);3
list1.Add(105);4

5
System.Collections.ArrayList list2 = new System.Collections.ArrayList();6
list2.Add("It is raining in Redmond.");7
list2.Add("It is snowing in the mountains.");8

但这种方便是需要付出代价的。添加到
另一个限制是缺少编译时类型检查;因为
1
System.Collections.ArrayList list = new System.Collections.ArrayList();2
// Add an integer to the list.3
list.Add(3);4
// Add a string to the list. This will compile, but may cause an error later.5
list.Add("It is raining in Redmond.");6

7
int t = 0;8
// This causes an InvalidCastException to be returned.9
foreach (int x in list)10
{11
t += x;12
}
尽管将字符串和 ints 组合在一个
在 C# 语言的 1.0 和 1.1 版本中,只能通过编写自己的特定于类型的集合来避免 .NET Framework 基类库集合类中的通用代码的危险。当然,由于此类不可对多个数据类型重用,因此将丧失通用化的优点,并且您必须对要存储的每个类型重新编写该类。
2 // No boxing, no casting:
3 list1.Add(3);
4 // Compile-time error:
5 // list1.Add("It is raining in Redmond.");
对于客户端代码,与
C#泛型的几个特点:
1、如果实例化泛型类型的参数相同,那么JIT编译器会重复使用该类型,因此C#的动态泛型能力避免了C++静态模板可能导致的代码膨胀的问题。
2 C#泛型类型携带有丰富的元数据,因此C#的泛型类型可以应用于强大的反射技术。
3、C#的泛型采用“基类,接口,构造器,值类型/引用类型”的约束方式来实现对类型能数的“显式约束”,提高了类型安全的同时,也丧失了C++模板基于“签名”的隐式约束所具有的高灵活性。
泛型类型的成员:
泛型类型的成员可以使用泛型类型声明中的类型参数,但类型参数如果没有任何约束,则只能在该类型上使用从System.Object继承的公有成员。
泛型接口:
泛型接口的类型参数要么已实例化,要么来源于实现类声明的类型参数。
泛型委托:
delegate bool Predicate<T>(T value);
class x {
static bool F(int i){..}
static bool G(string s){..}
static void Main()
{
Predicate<string> p2 = G;
Predicate<int> p1 = new Predicate<int>(F);
}
}
泛型委托支持在委托返回值和参数上应用参数类型,这些参数类型同样可以附带合法的约束。
泛型方法简介:
C#泛型机制只支持“在方法声明上包含类型参数”----即泛型方法
C#泛型机制不支持在除方法外的其它成员(包括属性,事件,索引器,构造器,析构器)的声明上包含类型参数,但这些成员本身可以包含在泛型类型中,并使用泛型类型的类型参数
泛型方法既可以包含在泛型类型中,也可以包含在非泛型类型中。
public class Finder{
public static int Find<T>(T[] items,T item){
for(int i=0 ;i<items.Length;i++)
{
if(..) reutrn i;
}
return -1
}
}
//泛型方法调用
int i = Finder.Find<int>(new int[] {1,2,3,4,5},6);
范型类型参数的约束:在定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的类型种类施加限制。如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误。这些限制称为约束。约束是使用 where 上下文关键字指定的。下表列出了六种类型的约束:
|
约束 |
说明 |
|---|---|
|
T:结构 |
类型参数必须是值类型。可以指定除 |
|
T:类 |
类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。 |
|
T:new() |
类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。 |
|
T:<基类名> |
类型参数必须是指定的基类或派生自指定的基类。 |
|
T:<接口名称> |
类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。 |
|
T:U |
为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束。 |

