Generics 认识

Generics 泛型

问题:
------
拿栈举例。
我们在实现一个Stack功能时,可能会这样写:

 

 

    public class IntStack
    {
        
int[] m_Items;

        
public void Push(int item)
        {
            
//
        }
        
public int Pop()
        {
            
//
        }
    }

 

调用代码为:

IntStack stack = new IntStack();
stack.Push(
1);
int number = stack.Pop();

 

当然,这是最开始学数据结构时的写法,如果栈内想存放string类型的值,我们只能重写一个类型为string的类来重新实现。

public class StringStack
{
   
string[] m_Items; 
   
public void Push(string item)
   {}
   
public string Pop()
   {}
}

 

调用代码:

StringStack stack = new StringStack();
stack.Push(
"1");
string number = stack.Pop();

 

以这种方式不会产生性能和类型安全问题,但却会引起同样严重的问题 — 影响工作效率。编写类型特定的数据结构是一项乏味的、重复性的且易于出错的任务。在修复该数据结构中的缺陷时,您不能只在一个位置修复该缺陷,而必须在实质上是同一数据结构的类型特定的副本所出现的每个位置进行修复。此外,没有办法预知未知的或尚未定义的将来类型的使用情况。

考虑一种普通的、存储各种类型的实例。在C#1.1里,我们能找到的解决办法就是使用Object,因为所有的类型都继承于Object,它们可以相互转换。
这样我们有了下面的代码。

public class Stack
{
   
object[] m_Items; 
   
public void Push(object item)
   {}
   
public object Pop()
   {}
}

 

调用代码:
int型:
Stack stack = new Stack();
stack.Push(1);
string number = (int)stack.Pop();
-----
Stack stack = new Stack();
stack.Push("1");
string number = (string)stack.Pop();


这意味着,在该堆栈中使用的内部数据类型是难以归类的 Object,并且堆栈方法与 Object 交互:
基于 Object 的解决方案存在两个问题。
第一个问题是性能。在使用值类型时,必须将它们装箱以便推送和存储它们,并且在将值类型弹出堆栈时将其取消装箱。装箱和取消装箱都会根据它们自己的权限造成重大的性能损失,但是它还会增加托管堆上的压力,导致更多的垃圾收集工作,而这对于性能而言也不太好。即使是在使用引用类型而不是值类型时,仍然存在性能损失,这是因为必须从 Object 向您要与之交互的实际类型进行强制类型转换,从而造成强制类型转换开销:
第二是类型转换安全。试想这样的代码出现。

Stack stack = new Stack();
stack.Push(
"abcd");
int number = (int)stack.Pop();

 


我们有可能在栈内引用很多种数据类型,所以我们将Stack内的元素定义为object 类型来解决。但同时也带来了更大的数问,值类型和引用类型转换(请看专题,待),这种转换是很耗费资源的。
但在C#1.1时,
尽管发现特定数据类型的方法不实用,而且object的类型也存在不少问题,但却不存在一个好的解决办法。

泛型
-----
在C#2.0中引入了泛型的概念,


本身实现
----
后编译。在Java中,先将T编译为Object类型。而C++中,刚为每一个实现生成相应的代码。。
C#中,真正的泛型实例化是发生在JIT编译时,生成不同的本地代码。查看IL可发现,T被编译为 [ ldtoken    !!T  ]
* ldtoken指令:将元数据标记转换为其运行时表示形式,并将其推送到计算堆栈上。!!T时编译器生成的一个占位符。
工作形式时这样的:第一次编译的时候,首先生成IL代码以及元数据,T只是一个隐藏的符号,这是并没有对泛型类型进行实例化,当JIT编译的时候,将以实际类型替换IL代码和元数据的T符号,并将其转换为本地代码。

泛型的好处
------------
通过泛型可以定义类型安全的数据结构,而无须使用实际的数据类型。这能够显著提高性能并得到更高质量的代码,因为您可以重用数据处理算法,而无须复制类型特定的代码。

.NET 中的泛型使您可以重用代码以及在实现它时付出的努力。类型和内部数据可以在不导致代码膨胀的情况下更改,而不管您使用的是值类型还是引用类型。您可以一次性地开发、测试和部署代码,通过任何类型(包括将来的类型)来重用它,并且全部具有编译器支持和类型安全。因为一般代码不会强行对值类型进行装箱和取消装箱,或者对引用类型进行向下强制类型转换,所以性能得到显著提高。对于值类型,性能通常会提高 200%;对于引用类型,在访问该类型时,可以预期性能最多提高 100%(当然,整个应用程序的性能可能会提高,也可能不会提高)。本文随附的源代码包含一个微型基准应用程序,它在紧密循环中执行堆栈。该应用程序使您可以在基于 Object 的堆栈和一般堆栈上试验值类型和引用类型,以及更改循环迭代的次数以查看泛型对性能产生的影响。


应用泛型
----------

public class Stack<T>
{
   T[] m_Items; 
   
public void Push(T item)
   {}
   
public T Pop()
   {}
}

 

Stack stack = new Stack(int);
stack.Push(
1);
int number = stack.Pop();
posted @ 2008-12-02 17:20  Binglingshui  阅读(325)  评论(1编辑  收藏  举报
小明明