日期:2011-05-25  浏览次数:20603 次

摘要

Anders Hejlsberg,C#的主架构师,与Bruce Eckel和Bill Venners 谈论了C#和Java的泛型、C++模板、C#的constraints特性以及弱类型化和强类型化的问题。

Anders Hejlsberg,微软的一位杰出工程师,他领导了C#(发音是C Sharp)编程语言的设计团队。Hejlsberg首次跃上软件业界舞台是源于他在80年代早期为MS-DOS和CP/M写的一个Pascal编译器。不久一个叫做Borland的非常年轻的公司雇佣了他并且买下了他的编译器,从那以后这个编译器就作为Turbo Pascal在市场上推广。在Borland,Hejlsberg继续开发Turbo Pacal并且在后来领导一个团队设计Turbo Pascal的替代品:Delphi。1996年,在Borland工作13年以后,Hejlsberg加入了微软,在那里一开始作为Visual J++和windows基础类库(WFC)的架构师。随后,Hejlsberg担任了C#的主要设计者和.NET框架创建过程中的一个主要参与者。现在,Anders Hejlsberg领导C#编程语言的后续开发。

2003年7月30号,Bruce Eckel(《Thinking in C++》以及《Thinking in Java》的作者)和Bill Venners(Artima.com的主编)与Anders Hejlsberg在他位于华盛顿州Redmond的微软办公室进行了一次面谈。这次访谈的内容将分多次发布在Artima.com以及Bruce Eckel将于今年秋天发布的一张音频光碟上。在这次访谈中,Anders Hejlsberg谈论了C#语言和.NET框架设计上的一些取舍。

·        在 第一部分:C#的设计过程中, Hejlsberg谈论了C#设计团队所采用的流程,以及在语言设计中可用性研究(usability studies)和好的品味(good taste)相对而言的优点。

·        在第二部分:Checked Exceptions的问题中, Hejlsberg谈论了已检测异常(checked exceptions)的版本(versionability)问题和规模扩展(scalability)问题。

·        在第三部分: 委托、组件以及表面上的简单性里,Hejlsberg 谈论了委托(delegates)以及C#对于组件的概念给予的头等待遇。

·        在第四部分:版本,虚函数和覆写里,Hejlsberg解释了谈论了为什么C#的方法默认是非虚函数,以及为什么程序员必须显式指定覆写(override)。

在第五部分:契约和互操作性里,Hejlsberg谈论了DLL hell、接口契约、strong anmes以及互操作的重要性。
在第六部分:Inappropriate Abstractions里, Hejlsberg以及C#团队的其他成员谈论了试图让网络透明的分布式系统,以及试图屏蔽掉数据库的对象——关系映射。  
在第七部分, Hejlsberg比较了C#和Java的泛型以及C++模板的实现方法,并且介绍了C#的constraints特性以及弱类型化和强类型化的问题。
泛型概述
Bruce Eckel: 能否就泛型做一个简短的介绍?

Anders Hejlsberg: 泛型的本质就是让你的类型能够拥有类型参数。它们也被成为参数化类型(parameterized types)或者参数的多态(parametric polymorphism)。经典的例子十九一个List集合类。List是一个方便易用的、可增长的数组。它有一个排序方法,你可以通过索引来引用它的元素,等等。现今,如果没有参数化类型,在使用数组或者Lists之间就会有些别扭的地方。如果使用数组,你得到了强类型保证,因为你可以定义一个关于Customer的数组,但是你没有可增长性和那些方便易用的方法。如果你用的是List,虽然你得到了所有这些方便,但是却丧失了强类型保证。你不能指定一个List是关于什么的List。它只是一个关于Object的List。这会给你带来一些问题。类型检测必须在运行时刻做,也就意味着没有在编译时刻对类型进行检测。即便是你塞给List一个Customer对象然后试图取出一个String,编译器也不会有丝毫的抱怨。直到运行时刻你才会发现他会出问题。另外,当把基元类型(primitive type)放入List的时候,还必须对它们进行装箱(box)。基于上述所有这些问题,Lists与Arrays之间的这种不和谐的地方总是存在的。到底选择哪个,会让你一直犹豫不决。

泛型的最大好处就是它让你有了一个两全其美的办法(you can have your cake and eat it too),因为你可以定义一个List<T>[读作:List of T]。当使用一个List的时候,你可以实实在在地知道这个List是关于什么类型的List,并且让编译器为你做强类型检测。这只是它最直接的好处。接下来还有其它各种各样的好处。当然,你不会仅仅想让List拥有泛型。哈希表(Hashtable)或者字典(Dictionary)——随便你怎么叫它——把键(keys)映射到值(values)。你可能会想要把Strings映射到Customrs,或者ints到Orders,而且是以强类型化的方式。

C#的泛型
Bill Venners: 泛型在C#中是如何工作的?

Anders Hejlsberg: 没有泛型的C#,基本上你只能写class List {...}。有了泛型,你可以写成class List<T> {...},这里T是类型参数。在List<T>范围内你可以把T当作类型来使用,当真正需要创建一个List对象的时候,写成List<int>或者List<Customer>。新类型是通过List<T>构建的,实际上就像是你的类型参数替换掉了原本的类型参数。所有的T都变成了ints或者Customers,你不需要做类型转换,因为到处都会做强类型检验。

在CLR(Common Language Runtime)环境下,当编译List<T>或者其它任何generic类型的时候,会像其它普通类型一样,先编译成中间语言IL(Intermediate Language)以及元数据。理所当然,IL以及元数据包含了额外的信息,从而可以知道有一个类型参数,但是从原则上来说,generic类型的编译与其它类型并没有什么不同。在运行时刻,当应用程序第一次引用到List<int>的时候,系统会查找看是否有人已经请求过List<int>。如果没有,它会把List<T>的IL和元数据以及类型参数int传递给JIT。而JITer在即时编译IL的过程中,也会替换掉类型参数。

Bruce Eckel: 也就是说它是在运行时刻实例化的。

Anders Hejlsberg: 的确如此,它是在运行时刻实例化的。它在需要的时候产生出针对特定类型的原生代码(native code)。从字面上看,当你说List<int>的时候,你会得到一个关于int的List。如果generic类型的代码使用了一个关于T的array,你得到的就是一个关于int的array。

Bruce Eckel: 垃圾回收机制会在某个时候来回收它么?

Anders Hejlsberg: 可以说会,也可以说不会,这是一个正交的问题。这个类在应用程序范围内被创建,然后在这个应用程序范围内就一直存在下去。如果你杀掉这个应用