UML软件工程组织

C# FAQ:关于泛型
来源:Microsoft C# Team 作者:Eric Gunnerson

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#中没有这种特性。

 

 

版权所有:UML软件工程组织