- 在同一个应用程序域中的两个Assembly如果都是用到了同一种类型List<DateTime>, 那么这两个在CLR中只会编译一次。
- 所有以引用类型作为参数的, 这些code都可以被共享, 因为不管是List<String> 还是List<Stream>, 他们都是一个指针(要么是32bits, 要么是64bits), 这个指针指向存储在堆上的数据。 所有的指针都是相同的操作(即CPU指令相同)。
为什么泛型?
面向对象的语言好处是使得程序员可以很方便地重用代码, 在面向对象中程序员可以写一个子类去继承自一个父类,写不同的程序能够重用以前的代码, 会减少很多代码的工作量。
但是算法能不能重用呢? 答案是肯定的, CLR提供了一个算法重用的机制, 就是泛型。
算法重用的例子, 参考List<T>类型, 在System.Collections.Generic中定义。
泛型类型和继承
我们使用两段代码来说明:
Internal sealed class Node<T>
{
Public T m_data;
Public Node<T> m_next;
Public Node(T data):this(data, null)
{
m_data=data;
}
Public Node(T data, Node<T> next)
{
m_data=data;
m_next=next;
}
Public override String ToString()
{
return m_data.ToString() + ((m_next!=null)? m_next.ToString(): String.Empty);
}
}
有了这样的类型以后, 我们就可以去建造一串有字符组成的链。
Private static void SameDataLinkedList()
{
Node<Char> head=new Node<Char>('C');
head=new Node<Char>('B', head);
head=new Node<Char>('A', head);
Console.WriteLine(head.ToString());
}
上面是的链上的数据都是同一类型的, 如果要把不同类型的数据放在一个链里, 可以怎样实现呢? 是的, 可以使用Object 作为类型的参数传进去, 代码如下。
Private static void DifferentDataLinkedList()
{
Node<Object> head=new Node<Object>('C', null);
head = new Node<Object>(DateTime.Now, head);
head= new Node<Object>("This is String", head);
Console.WriteLine(head.ToString());
}
但是, 如果是Object的类型的话, 我们值类型的数据要传给Object的参数会使用装箱操作, 对性能的影响非常大。还有一种方法就是使用泛型的继承实现, 性能会提高很多, 代码如下:
Internal class Node
{
Protected Node m_next;
Public Node(Node next)
{
m_next=next;
}
}
Internal class TypeNode<T>: Node
{
Public T m_data;
Public TypeNode(T data, Node next):base(next)
{
m_data=data;
}
Public TypeNode(T data): this(data, null)
{
m_data=data;
}
Public override String ToString()
{
return m_data.ToString() + ((m_next!=null)? m_next.ToString(): String.Empty);
}
}
有了以上代码, 我就可以这样定义我的链了:
Internal static void DifferentDataLinkedList()
{
Node head=new TypeNode<Char>('C');
head= new TypeNode<DateTime>(DateTime.Now, head);
head=new TypeNode<String>("This is String", head);
Console.WriteLine(head.ToString());
}
这样实现的泛型继承的好处: 类型安全 和 不需要装箱。
泛型标识
泛型的写法的确是麻烦, 一堆的< 和 >, 不管是读和写都很麻烦, 有什么方法能走点近道呢? 答案有两种方法: 继承 和 使用Using 关键字。
如果我们在程序中用的了List<DateTime> dtl=new List<DateTime>(); 这样的代码很多的话, 我们就能写一个DateTimeList的类来继承List<DateTime>
Interanl class DateTimeList: List<DateTime>{
//这里边不需要写代码
}
所以在程序中我们就可以使用DateTimeList来替换所有使用到List<DateTime>的地方。
第二种方法使用Using关键字
我们可以给List<DateTime>声明一个别名DateTimeList, 如下代码:
Using DateTimeList=System.Collections.Generic.List<System.DateTime>;
然后我们就可以在程序中使用DateTimeList这个类型了。
两种方法的区别是第二种方法中, DateTimeList和List<DateTime>完全等价, 而在第一种方法中, 有如下注意点: 任何使用类型List<DateTime>的地方都可以使用DateTimeList替换, 但是使用DateTimeList的地方不能使用List<DateTime>替换, 因为两者是继承关系而非等价关系。
优化代码编译过程
注: 值类型就不是这样的, 因为值类型的大小不一样, 即使大小一样的话, 操作这些值的指令也不同。