(本文来自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]
看来还是像java啊。
比较不错. 不过不明白如何用反射来实现a+b?
蔡学墉的观点是,Java虽然也有Generics的概念,但不是C#的Generics,但我还不太清楚他们的细节
搞错了,呵呵,不是Generics,是Metadata,Java的和C#的不同