例子
1using System.Collections.Generic;
2using System.Collections;
3namespace TestGeneric
4{
5 class Stack<T>
6 {
7 private T[] store;
8 private int size;
9
10 public Stack()
11 {
12 store = new T[10];
13 size = 0;
14 }
15
16 public void Push(T t)
17 {
18 store[size++] = x;
19 }
20
21 public T Pop()
22 {
23 return store[--size];
24 }
25 }
26}
2using System.Collections;
3namespace TestGeneric
4{
5 class Stack<T>
6 {
7 private T[] store;
8 private int size;
9
10 public Stack()
11 {
12 store = new T[10];
13 size = 0;
14 }
15
16 public void Push(T t)
17 {
18 store[size++] = x;
19 }
20
21 public T Pop()
22 {
23 return store[--size];
24 }
25 }
26}
原来用到的集合,栈,ArrayList之类的集合,会遇到你用的是什么类型的这个问题,从oo的来看,是存为根类型,也就是object。它可以支持各种各样的操作-通用化。在高级语言中,譬如类,他们都是托管类型,也会有轻型的值类型,譬如 int byte,当然也可以使用自己 struct 结构类型。在添加到集合时,他们都会面临转到object时的装箱类型,需要复制到数据然后包装到托管堆中。这会严重的影响性能。还有就是涉及到了安全问题,譬如可以在ArrayList中可以塞入各种类型,但是出来的时候必须要小心,它需要我们将其转换为原类型,然后才能调用它自己的方法。
有了泛型之后就会方便很多,所谓泛型,就是通过参数化类型来实现在同一份代码上操作多种数据类型,好像是“模板”,它是一种编程范式,C++ java中好像都这么个概念,它利用“参数化类型”将类型抽象化,泛型其实就是一种抽象类型-即不完整的类型,只有具体化了后才能new出一个对象。优点:达到了类型的安全,复用,更高效—不需要boxing,unboxing,更清晰的更严格的约束—在编译时检查。这是一种“类型“的多态,而virtual,override,还有重载,通过继承而实现“对象”的多态。上面的例子的多参数化,是后绑定,也就是不属于某个特定类型的Stack。它可以实例化出无数个具体类型。这就是实现了复用,软件开发最强调的就是复用,客户之间的软件拷贝是一种复用,而泛型提供了软件内部复用范式的机制。
编译过程:
1、第一次编译时,编译器只为Stack<T>类型产生“泛型版本”的IL代码与元数据,并不进行实例化,T在中间只是充当占位府。
2、JIT编译时,第一次遇到Stack<int>的时候,将用int 替换“泛型版本”的IL代码与元数据的T,进行泛型类型的实例化,当第二次遇到Stack<int>,jit就不在编译了,而是直接找到已经生成好的在内存中的类型。它在内存中动态的生成一个类型,而不是保存在已经编译好的exe或dll文件中。
3、CLR为所有的类型参数为“引用类型”的泛型产生同一份代码,但如果是值类型,CLR将为每一个不同值类型产生一份代码。因为引用类型变量本身不保存值,而是一个指针,保存的是一个32位的地址。所以在实例一个具体类型的时候就可以使用同一份代码,他们共享的是同一份泛型代码,不用去确定类型。其实也是转化成了object。这点不会优化性能,因为引用类型本身就在堆了,而所说的优化性能是指值类型不再需要装箱拆箱了。大概就是这个意思,具体就不太清楚了。
4、就是说,对于引用的泛型类型时性能是没有提高的,只是“显示的约束”而提高了类型安全性。但是在值类型的集合什么的,只要涉及到将值类型转移到托管堆中,装箱拆箱什么的,使用泛型会有提高性能的表现。
特点:
c#的泛型,只支持泛型方法(泛型类,非泛型类中均可)的声明类型参数,但不支持其他成员,即本身不能在譬如泛型事件,泛型属性、索引器声明泛型类型参数。但是可以在泛型类中,使用泛型类型的参数。
约束:
基类约束
Class A { public void F1() {...}}
Class B {public void F2() {...}}
Class C<S,T>
where S:A //S继承自A
where T:B //T继承自B
{
S.F1(); //类型为S的变量可以调用F1
T.F2();
}
接口约束
类似
构造器约束
只支持无参的构造器约束
Class A{ public A() {} }
Class B{ public B(int i) {}}
Class C<T>
where T : new()
{
T t = new T()
}
// C<A> c = new C<A> right
// C<B> c2 = new C<B> error
值类型与引用类型约束
public struct A {...}
public class B {...}
class C<T>
where T : struct
{
//T在这里面试一个值类型
}
C<A> c = new C<A>(); //right
C<B> c = new C<B>(); //error
1public class Racer
2{
3 private string name;
4 public string Name
5 {
6 get { return name; }
7 }
8
9 private string car;
10 public string Car
11 {
12 get { return car; }
13 }
14
15 public Racer(string name, string car)
16 {
17 this.name = name;
18 this.car = car;
19 }
20
21 public override string ToString()
22 {
23 //return base.ToString();
24 return name + " , " + car;
25 }
26}
27
28class Test
29{
30 static void Main()
31 {
32 List<Racer> racers = new List<Racer>();
33 racers.Add(new Racer("jim","BMW760Li"));
34 racers.Add(new Racer ("huoxingren","Benz500"));
35 racers.Add(new Racer("zhuhee","xiali"));
36 racers.Add(new Racer ("yanhao","Benz500"));
37
38 foreach(Racer r in racers)
39 {
40 Console.WriteLine(r.ToString());
41 }
42
43 ArrayList racers2 = new ArrayList();
44 racers2.Add(400);
45 racers2.Add(new Racer("hehe","jppe"));
46 foreach(int i in racers2)
47 {
48 Console.WriteLine(i.ToString());
49 }
50 }
51}
优点:可以明显的看到,使用泛型增强了安全性,在编译时就会检查数据类型,而传统ArrayList只能在运行时才能发现错误,导致异常,虽然泛型失去了一些灵活性,但是安全第一!2{
3 private string name;
4 public string Name
5 {
6 get { return name; }
7 }
8
9 private string car;
10 public string Car
11 {
12 get { return car; }
13 }
14
15 public Racer(string name, string car)
16 {
17 this.name = name;
18 this.car = car;
19 }
20
21 public override string ToString()
22 {
23 //return base.ToString();
24 return name + " , " + car;
25 }
26}
27
28class Test
29{
30 static void Main()
31 {
32 List<Racer> racers = new List<Racer>();
33 racers.Add(new Racer("jim","BMW760Li"));
34 racers.Add(new Racer ("huoxingren","Benz500"));
35 racers.Add(new Racer("zhuhee","xiali"));
36 racers.Add(new Racer ("yanhao","Benz500"));
37
38 foreach(Racer r in racers)
39 {
40 Console.WriteLine(r.ToString());
41 }
42
43 ArrayList racers2 = new ArrayList();
44 racers2.Add(400);
45 racers2.Add(new Racer("hehe","jppe"));
46 foreach(int i in racers2)
47 {
48 Console.WriteLine(i.ToString());
49 }
50 }
51}