.NET Generics VS C++ Templates
刘未鹏 /文
C++的罗浮宫(http://blog.csdn.net/pongba)
在C++中还没有引进模板的时候,C++STL之父stepanov就敏锐的发觉,面向对象理论并不能描述现有的所有结构,比如,算法就不是一个对象,再深入下去,他发觉有很多算法的抽象逻辑并不依赖于它所作用的对象以及底层数据结构的组织方式,也就是说,算法的逻辑可以抽象出来单独描述,于是他试图用当时的C++来描述这种抽象,而在当时,C++还不支持泛型,结果导致这种对算法抽象描述非常繁复而低效,与此类似的结构还有容器(算法和容器是STL的两大组成部分,两者通过迭代器“粘合”起来)。后来C++中加入了模板,不得不说很大程度上是stepanov努力的结果。这种想法在当时面向对象理论正一手遮天的情况下不得不用“天才”来形容,它直接影响了C++的“后半生”。
泛型可以在几乎无损于效率的情况下支持代码的高度可复用性,并且具有数学的抽象美(这一点接触过STL源码的人都非常清楚)。于是,泛型几乎立即就成了C++中举足轻重的特性之一。
而现在,.NET和java也意识到泛型的重要性——尽管它们有单根的特性,但是,基于继承的算法抽象描述注定是“强绑定”的,低效的,面向对象的多态的额外负担在此无可逃避。但是,基于泛型的算法抽象描述却是“非绑定”的,高效的,任何符合语法和语义要求的对象都可以作为参数传递给它,泛型的本质就是为每一种对象生成一份单独的代码(也就是所谓的“实例化”),这一份量身顶做的代码具有很高的效率,是强类型的,不需要运行期多态的支持和负担,所以.NET和java中引入泛型可以说是顺理成章的。只不过,由于.NET和java的语言本质(解释型),所以其中的泛型不能像C++那样淋漓尽致,有一些很难逾越的技术鸿沟,这在下面会提到。
下面就来说说.NET中的泛型实现。至于其语法是次要的,对C++模板稍有了解的人不难从几行示例代码中看出来。
其实,泛型的实质是相当简洁和直观的,了解宏的人很清楚,在除去类型安全的前提下,一些简单的泛型代码可以用宏来模拟。这也就是说,编译器负责为用户提供的每一集模板参数实例化出一份模板的实体。
也许你会说,这不是和C++的几乎一样吗?是的,思想是一样的,但是,实现却有本质的不同。
.NET的泛型代码的实例化是在运行期进行的,这一点与C++有本质上的不同。在.NET泛型中,处在编译期的只是一个简单的引用,而对相应的类的实例化在运行期由CLR运行层来进行,所以.NET泛型是平台的一部分,而不像C++那样,是编译器的一部分。
在运行期由CLR进行的实例化给.NET泛型带来一个显著的好处:避免代码膨胀。在C++中,位于两个不同编译单元中的模板实例化很可能导致两份完全相同的代码,而且,由两集不同模板参数实例化出来的模板实例可能无法共享某些代码。但是.NET中,实例化时CLR知道一切,所以不会出现在两个Assembly中出现两份相同的实例化代码的情况,并且,一个Assembly中实例化的代码可以被用于另一个Assembly,甚至同一个模板的不同实例间也可以共享某些代码。这种代码共享的特点可以某程度上缓解代码膨胀。
但是,.NET的泛型也有一些弱点。在运行期实例化的特点决定了它无法进行某些复杂的类型推导(这将导致JIT速度缓慢),也就是说,像STL和Boost库中广泛运用的traits技术在.NET中注定要嗑磕碰碰。而支持复杂的类型推导技术的语言特性是偏特化,这也就解释了为什么.NET目前还没有支持偏特化的原因(甚至连特化也不支持)。但是,traits却又是精巧的架构和效率所必须的[1]。所以,在(运行期)效率上,.NET无法与C++比肩。
另外,.NET泛型支持constraint(约束),所谓constraint,就是给模板形参加上约束。在.NET中这些约束主要是诸如:继承自某个基类,实现了哪些接口,以及是否含有无参的public构造函数。在.NET中引入约束是有理由的:如果不引入约束,则有两种情况:第一,对于某些模板实参,实例化代码不能通过JIT编译,又因为泛型代码的编译是在运行期由CLR运行层进行的,所以彼时的编译错误可能得通过异常的方式来反映[2],然而更重要的是,这可能导致很多无谓的编译时耗,也就是说,CLR可能在编译了许多代码后发现一个编译错误,于是以前的编译都是在浪费时间。第二,解决第一种情况的方法是强制转换,例如,如果你想要对某个对象(该对象的类型是模板形参)调用CompareTo,你得先把该对象转换为ICompareable接口。这虽然能够解决问题,但是又增加了不必要的运行时开销。所以C#采用constraint,这样,如果形参不符合要求(例如,没有特定的成员函数),则在编译期就能够发现,既保证了编译期的强类型检查,又减小了运行期开销。作为比较,C++并不支持constraint,至于原因Stroustrup在D&E里说的很清楚。C++并不存在上面所说的问题,并且C++中的constraint可以通过其它方式来模拟[3]。
从总体上来说,.NET与C++“道不同”,层次也不一样,.NET中的泛型有这些简单的特性对于它已经够用了,因为.NET在运行期的信息远比C++要多(再加上单根继承的优势),所以一些工作可以交给运行期的类型识别或reflection来做(不用在编译期进行复杂的类型推导),并且.NET的语言特性相对少而简洁,这就避免了一些繁杂的工作。而C++作为编译型的语言,一经编译便几乎丧失所有结构信息和类型信息(typeinfo只包含了相当少的一部分信息),所以,C++必须在编译期打点好一切,从而,C++模板的众多特性是不可忽缺的。读者不妨将.NET的delegate与Boost库中的boost::signal对比一下,看看后者为了实现一个完善的“回调”系统做了多少工作。而前者有单根结构和reflection的支持,其实现要简单得多。
所以,.NET泛型虽然简单,但却是量身定做,而C++泛型虽然复杂,却精巧而必要。二者各取所需,在各自的语言中都会有大量的用武之地。至于孰优孰劣是语言专家的事情,这里的对比是想让读者对两者有个大体的把握和理性的认识。
分享到:
相关推荐
《Packt.NET.4.0.Generics.Beginner's.Guide.2012》这本书是为初学者准备的一份全面指南,旨在介绍.NET Framework 4.0中的泛型技术。泛型是.NET编程中一个极其重要的特性,它允许开发者创建可重用的类型安全的代码,...
This new edition of Pro C# 5.0 and the .NET 4.5 Platform has been completely revised and rewritten to reflect the latest changes to the C# language specification and new advances in the .NET Framework...
《Professional .NET 2.0 Generics》是.NET框架2.0版本中关于泛型的一本深入解析书籍,其中包含了丰富的代码示例。泛型是.NET Framework 2.0引入的一项重要特性,它允许开发者创建可重用的类型化组件,以提高代码的...
This new 7th edition of Pro C# 6.0 and the .NET 4.6 Platform has been completely revised and rewritten to reflect the latest changes to the C# language specification and new advances in the .NET ...
.NET Framework 2.0引入了许多新特性,如改进的性能、更强的安全性以及对 generics 的支持。 3. Web Forms:ASP.NET 2.0的核心组件之一,它允许开发者使用控件和事件驱动编程模型来创建Web页面,类似于Windows应用...
2.ASP.NET.2.0.高级编程(第4版) [1/7] 原书名: Professional ASP.NET 2.0 原出版社: Wrox 作者:(美)Bill Evjen, Scott Hanselman, Farhan Muhammad [同作者作品] [作译者介绍] 译者: 李敏波[同译者作品] ...
- **Generics**: 讲解了泛型的概念及其在代码复用中的作用。 - **Decorators**: 再次强调了装饰器在 TypeScript 和 Angular 中的重要性。 - **TypeScript and Angular**: 探讨了 TypeScript 如何增强 Angular ...
《.Net 2.0 泛型高级编程》是一本由Tod Golding编著的专业技术书籍,专注于探讨.NET Framework 2.0中的泛型特性。泛型是.NET框架中的一个重要概念,它允许开发者创建可重用的类型安全的数据结构和算法,极大地提高了...
Chapter 53: C#, Visual Basic, C++/CLI, and F# . . . . . . . . . . . . . . . . . . . OC157 Chapter 54: .NET Remoting . . . . . . . . . . . . . . . . . . . . . . . . . . . .OC191 Chapter 55: Web ...
Chapter 53: C#, Visual Basic, C++/CLI, and F# . . . . . . . . . . . . . . . . . . . OC157 Chapter 54: .NET Remoting . . . . . . . . . . . . . . . . . . . . . . . . . . . .OC191 Chapter 55: Web ...
- **.NET 2.0**:这个版本的 .NET Framework 发布于2005年,引入了 Generics、Partial Classes 和 Anonymous Methods 等特性。Newtonsoft.Json 对其的支持意味着即使在较旧的项目中也可以使用 JSON 功能。 - **...
泛型(Generics)是JDK5引入的一种参数化类型特性。它提供了编译时类型安全检测机制。其本质是参数类型,所操控的数据类型被指定为一个参数。泛型不存在于JVM虚拟机。泛型学习、泛型限定类型、泛型通配符、泛型继承。...
Based on the provided information from the file "Addison.Wesley.C++.by.Dissection.2002.pdf," we can derive a comprehensive overview of the C++ programming language as covered in this text. The book is...
本资料 "[Java泛型和集合].(Java.Generics.and.Collections).Maurice.Naftalin&Philip.Wadler.文字版" 由知名专家Maurice Naftalin和Philip Wadler编著,提供了关于这些主题的深入理解。 **Java泛型** 是自Java...
这样的接口单元使得开发者无需直接与GDI+的C++原生接口打交道,而是通过Delphi的类和方法进行操作,大大简化了编程过程。 描述中的"完全接口形式调用GDI Plus,更简单"表明这个接口单元提供了全面的GDI+功能,并且...