c#泛型与其他语言的对比(深入理解c#)

1.同c++模板的对比:

c++模板有点像是发展到极致的宏。他们非常强大,但代价就是代码膨胀和不易理解。

在c++中使用一个模板时,会为那一套特定的模板实参编译代码,好在模板实参本来就在源代码中一样。这意味着对约束的需求就不像对编译器需求那么多,因为编译器在为这一套特定的模板实参编译代码时,会检查你可以对类型执行哪些处理。不过,c++标准协会已经意识到约束仍然是有用的。约束又被包含进来,然后在c++11中删除了。

c++编译器具有一定的智能,针对任何给定的一套模板实参,代码都只会编译一次。但是,它还没有聪明到能共享代码的程度,就像CLR对引用类型做的那样,不过,不共享代码也是有好处的——它允许进行类型特有的优化,比如为一部分类型参数内联方法调用。但是,针对来自同一个模板的另一部分类型参数,则不内联方法调用。它还意味着能单独为每一套类型参数执行重载决策。而在c#的情况下,只能根据由现有约束为c#编译器提供的有限信息进行一次重载决策。

不要忘记,对于普通c++来书,编译是一次完成的,而不是想.NET模型那样,先编译成IL,再由JIT编译成本地代码。一个c++程序以10种不同的方式使用一个标准模板,就会在程序中包含代码10次。但是,在c#中,一个类似的程序如果以10种不同的方式使用来自框架的一个泛型类型,那么根本不会包含泛型类型的代码。相反,它只是引用一下泛型类型。执行时需要多少个不同的版本,JIT就会编译多少个。

c++模板比c#泛型好的一个地方在于,模板实参不要求必须是类型名,变量名,函数名和常量表达式也是允许的。一个常见的例子就是使用一个缓冲区类型,他将缓冲区的大小作为模板实参之一。所以,buffer<int,20>是包含20个int值的缓冲区,而buffer<double,35>是包含35个double值的缓冲区。这个功能对于模板元编程是至关重要的。作为一种高级的c++技术,模板元编程的基本思路令我感到害怕。但放在专家的手里,他又确实能发挥出强大的作用。

2.同Java泛型的对比:

c++模板为模板生成的代码要比c#为泛型生成的代码多一点。而Java生成的则要少一些。事实上,Java运行时根本不知道有泛型的存在。在为泛型类型生成的Java字节码(bytecode,大致和IL等价的一个术语)中,包含一些额外的元数据,来表示这是一个泛型。但是,在编译之后,负责调用的代码根本就发现不了曾有泛型出没的迹象。另外有一点可以肯定,泛型类型的实例只知道他自己非泛型方面的情况。例如,HashSet<E>的实例并不知道他自己被创建成一个HashSet<String>还是HashSet<Object>。编译器只是在必要的地方添加强制类型转换,并执行更稳妥的检查。可以将泛型类型作为一个“原始”类型使用,它相当于为每个类型实参都使用java.lang.Object。这种重写(会丢失信息)被称为类型擦除。java没有用户自定义的值类型,但就连内建的值类型也不能作为类型实参使用。相反,必须使用“已装箱”的版本,例如,对一个整数列表来说,就是ArrayList<Integer>。

Java泛型特性:

运行时虚拟机不知道关于泛型的一切,所以只要没有使用旧版本中不存在的类或方法,那么即使在代码中使用了泛型,编译之后代码一样可以在旧版本上运行。.NET的版本控制总体来说要严格的多——对于你引用的每个程序集,都可以指定版本号是否必须精确匹配。除此之外,生成时指定要在2.0CLR上运行的代码不能在.NET1上运行。

不需要学习一套新的类就可以使用Java泛型。非泛型开发者仍然使用ArrayList,泛型开发员只需使用ArrayList<E>。现有的类可以轻松的升级到泛型版本。

以前的特性通过反射系统被有效的利用——java.lang.Class(system.type的等价物)是泛型,它允许对编译时类型安全进行扩展,以覆盖涉及反射的许多情形。然而在其他一些情况下,他也会带来不便。

java使用通配符来支配协变性和逆变性。

posted @ 2018-09-20 11:52  mc宇少  阅读(606)  评论(0编辑  收藏  举报