C# FAQ:关于泛型

Posted: 三月 16th, 2004 | Author: | Filed under: 胡编 | 3 Comments »

(本文来自Microsoft C# Team的FAQ Blog。我会尽量跟踪这个站点,并不断增补内容。)


Q: C#泛型与C++ templates相比如何?


A: 这个问题相当复杂。


Anders曾在一篇访问中提到这个话题。


需要说明的是,泛型和templates的目的并不一样。有些工作templates做起来比泛型好,反之亦然。


模型(Model)


C++ templates使用编译时模型。当在C++程序中使用templates时,如同有一个微宏处理器在起作用一般。


C#泛型不只是编译器的一个特性,也是运行时环境的特性。类似List<T>这样的泛型类型,在编译后仍保有其泛型特质。换言之,C++编译器的替换工作,在C#泛型世界中是由JIT完成的。


错误检查


通过例子能最好地说明。考虑一个template,其中包括方法如下:


T Add(T t1, Tt2)
{
    return t1 + t2;
}


C++编译器能正确解析此方法。当template被真正地调用时,“T”会被真实类型所替代。设若使用的是int类型,则编译器能正确创建Add方法,因为它明白两个整数如何相加。


但如果使用类似“Employee”这样的类型,编译器将出错,因为它不了解如何相加两个“雇员”。


泛型则不然。因为在编译时并不知道具体类型,所以必须通过泛型类的方式来让编译器了解类型的额外信息。


这通过约束(constraints)来实现。Contraints允许作者规定泛型类型支持的数据类型。


例如:


Class List<T> where T:IComparable


意味着无论何时使用T,都可以往CompareTo()函数中传入T并调用该函数。


约束能提供与templates几乎相当的灵活性,但需要很复杂的约束语法。对于Whidbey,约束只能规定某些操作。


例如,无法约定泛型类型必须拥有一个add操作符,所以不能在泛型类中写“a+b”。


可以在运行时使用反射来实现,但实现上味道不清爽,也会带来性能损失。在未来版本中也许会解决这个问题。


运行时操作


C#泛型拥有完全的运行时支持。你可以在泛型类型上执行反射,在运行时创建泛型类型。在C++中没有同等功能。


空间使用


C++和C#对空间的使用不一样。C++ templates在编译时完成,每种类型都会在编译代码中占用独立空间。


在C#中,对特定类型的实现在运行时创建。当运行时环境创建类似List<int>这样的类型,JIT将查看是否之前创建过这种类型。如果已创建过,将直接使用创建了的代码。如果没有创建过,将获取编译器创建的IL代码,用真实类型进行替换。


其实这并不完全正确。对于每种值类型,都会有单独的本地代码路径。引用类型可以互相分享其实现。


所以,C#泛型占用较少磁盘空间和内存空间,比C++ Template有优势。


实际上,C++ 连接器有一种名为“template folding”的特性。C++连接器寻找同样的本地代码段,如果找到,则把它们放到同一目录。


Template元编程


C++ templates有时用于template元编程(metaprogramming)。C#中没有这种特性。


[作者:Eric Gunnerson]


3 Comments on “C# FAQ:关于泛型”

  1. 1 virushuo said at 12:48 上午 on 三月 19th, 2004:

    看来还是像java啊。

  2. 2 http:// said at 2:06 上午 on 十二月 8th, 2004:

    比较不错. 不过不明白如何用反射来实现a+b?

    蔡学墉的观点是,Java虽然也有Generics的概念,但不是C#的Generics,但我还不太清楚他们的细节

  3. 3 http:// said at 3:46 上午 on 十二月 8th, 2004:

    搞错了,呵呵,不是Generics,是Metadata,Java的和C#的不同


Leave a Reply