Anders Hejlsberg 访谈 .-转载

楔子:

      我是从DELPHI一直走进.NET的,我对工程和实用性比算法看的重要的多,我认为工程更加可以产生出没敢;

我见过很多年纪比较大的程序员,埋头苦干,很少思考美学,可能是没有站到软件工程领域思考我们的程序;

站在市场的角度如何,JAVA的精髓学了多少,不是语法的,是工程化语言的思想的;

      就算头发都没有了,又如何?到都来,我们很多软件是为工程服务的,C++编写的科研领域除外;

在工程学的语言里面JAVA,C#最为突出,DELPHI是先驱者,不过也是一个失败者,因为安德尔斯的离开;

开发是美的,有一天我读了安德尔斯的访谈,突然感觉豁然开朗,真是伟大啊。

字字珠玑!

 

A Conversation with Anders Hejlsberg

 

1.The C# Design Process

 

http://msdn.microsoft.com/vcsharp/homepageheadlines/hejlsberg/

Designing with a Living Agenda

Bruce Eckel:我听说C#是一组人在一个房间里开发的

Anders Hejlsberg:是的,四年一直在同一个房间,现在每个星期一,星期三,和星期五,我们还在那个房间

Bruce Eckel: 我非常关注C#的设计过程。我直接或间接涉及过几种不同的语言的设计.Python来说,Guido van Rossum b被幽默地称为慈善的独裁者。

Anders Hejlsberg:就像是我现在的位置

Bruce Eckel: 你是C#的独裁者

Anders Hejlsberg:事实上我是裁判,要是我们在一个问题周旋太久,是时候作决定了,我来决定;一般情况下决定是明显的

Bruce Eckel: 是不是和Turbo Pascal Delphi的设计一样

Anders Hejlsberg: 那些就不是很正规了,Turbo Pascal基本上是我一个人设计的。我和Chuck Jazdzewski Gary Whizin设计了Delphi,但是小组太小,似乎不需要正规的过程。而C#,是有一个正规的过程的:每个星期一,星期三,和星期五的一点到三点,我们要开一个正式的定期会议。问题冒出水面,然后解决它。在我们的内部网里的wiki上有一个问题列表,一个一个解决他们,等等。

Bruce Eckel:问题是如何浮出水面的

Anders Hejlsberg:哦,这是我们做的。用户可以通过很多方式-设计评论,新闻组,在语言方面给我们反馈。这些反馈产生问题,错误,矛盾,病态。我们就知道我们要做什么。我们反复研究列表上的问题,我们看准一个,问:“对于这个我们是不是有新想法了?没有,好,这个东西已经躺在这里几个星期了,让我们花三十分钟着手处理它吧,看看这次能处理到什么程度”

Bruce Eckel:那么当这件事情看起来很糟糕了呢

Anders Hejlsberg: 或者事情看起来还是挺好的,在下一个版本中你想用它,那么你继续解决。我认为这是一个确保没有东西遗漏的方法。你把所有的事情都放在列表上,有些事情你当时可能决定不去做,所以一直呆在列表上,但是最终它会被再次注意到;你可能去解决或者不,但是它不会丢失。

Bill Venners:C#的设计小组有哪些人,各担任什么角色

Anders Hejlsberg:最初的设计小组有Scott Wiltamuth, Peter Golde, Peter Sollich, Eric Gunnerson,和我,C#20的设计小组包括Peter Hallam, Shon Katzenberger, Todd Proebsting和我。最终还要感谢微软研究院的Don Syme Andrew Kennedy

可用性还是审美哲学

Bill Venners: C#的设计有多少是基于可用性研究,有多少是市场选择,有多少是审美的选择呢?

Anders Hejlsberg:无疑,好的语言设计归结于组合一个有共同品味的团队。归结于编程美学,象你说的。好的品味是非常主观的,很难定义,但是当你碰到它你就会认出它。我认为好的品味会给你带来许多可用性研究所不能给你的,因为可用性研究是非常高层次的。

一个调查可能问:“你认为这个特征怎么样?”但是却不好问,“你认为这个语言怎么样?”你从哪里开始,你怎样通过两个小时的可用性研究解决它,这是不可能的。

Bruce Eckel: 有些人想理解的深一点

Anders Hejlsberg: 用一种语言是一个渐入的过程。只有你用了几个月以后,你才能真正认识这种语言。你才能渐渐意识到哦,这真舒服“,你不能很快就意识到。也就是说,我们做了很多可用性研究,但是结果还是定位于某种特征。

Bill Venners:例如

Anders Hejlsberg:大部分是IDE的可用性研究。我们可能问,“人们能理解右击做这些和哪些吗?“,我们做过一些关于纯语法方面的可用性研究,比如属性和事件,但是真的不必要的。我认为对语言特征的可用性研究不象对IDE那样的受益。IDE是非常交互的。你能观察用户右击菜单,得到很好的反馈。对编程语言来讲,问题就更深了,“它的概念容易理解吗?“,你可以成立用户咨询委员会,还有回音板,把它放到你能发布的地方,你说,”这是我们关于这个新特征的想法,你们怎么看”,你希望他们提的愈多越好,因为在提交这个特征之前,你想知道更多的问题。所以除非一个语言特征已经完全经受考验,我们还要利用这种回音板。

 

2.The Trouble with Checked Exceptions

Checked Exceptions保持中立

Bruce Eckel: C#没有checked exceptions,你们是如何决定是否加入checked exceptions的呢?

Anders Hejlsberg:我看到checked exceptions的两个大问题:可升级和版本控制。我知道你也写了一些关于checked exceptions的文章,你和我们想的是一致的。

Bruce Eckel: I used to think that checked exceptions were really great.

我过去认为checked exceptions是很了不起的。

Anders Hejlsberg:坦白地说,它一出来的时候真的很了不的,观念上也没有错误。我完全同意checked exceptions是一个极好的东西,但就是具体用起来就有点问题了,拿Java的实现来说,有引出了另外的问题,实际上它没有让我的生活更简单,只不过换汤不换药。

Bruce Eckel:C#的设计团队里没有异议吗

Anders Hejlsberg: 没有,一致同意

基本上C#checked exceptions上是保持沉默的。一旦更好的方案发现-相信我我们一直在思考-我们可以退回去,然后再加入它。我深信如果你没有确凿的理由,又不能推动技术进步,最好保持沉默和中立,不加到框架中。

你如果叫一个新手去写一个日历控件,他就会自己想,‘哦,我要写一个世界上最好的日历控件,它具有多种形态,它有显示页,调节器,这个那个的。他们把所有的基础构造都放进去,然后花两天用它写一个应用程序。他们想,’下一个版本,我将做更多‘

接着他们开始考虑如何具体实现他们的抽象设计,然后发现自己的设计完全错了,把自己推入困境,不得不放弃。我不知道见过多少次这种悲剧了。我深信要保持最低要求。除非你真的要解决一个一般的问题,不要为了特殊的问题而改动框架。因为你不知道框架的最终样子是什么样。

Bruce Eckel:极限编程说,‘做最简单的事情,只要它能工作’

Anders Hejlsberg:是的,爱因斯坦说,‘尽可能做最简单的事,但是不要简单化’

我对checked exceptions的顾忌就是它给程序员带来了不便。你拿到一簇新的带有throws语句的APIs,你的代码将变得非常费解;你会认识到checked exceptions是没有帮组的。好像这些独裁的API设计者将指导你如何捕捉异常似的。他们是没权这样做的。

Versioning with Checked Exceptions

Bill Venners:你提到你对checked exceptions在可升级和版本控制方面的顾虑。你能具体说一下这两个问题吗?

Anders Hejlsberg: 首先,版本问题,这个很容易看到。比如,我有一个方法foo声明抛出异常A,BC。在foo的第二版中,我加了一些特征,现在它还要抛出异常D。但是这个更改是具有破坏性的,因为已有的调用者肯定没有捕捉这个异常。

在新版本里加一个新的异常就破坏客户代码,就像在接口里加新的方法。一旦你发布一个接口,无论如何是不能变动的,因为所有用到它的程序里都有可能有你想加到下个版本里相同的方法。所以你只能创建一个新的接口代替。和这个相似,你也要重建一个新的方法可以抛出更多的异常的foo2,或者你得在新foo里捕捉异常D,然后转换成,ABC

Bill Venners:但是好像怎么样都要破坏代码,甚至在一个没有checked exceptions的语言里。如果在新版本的foo里抛出了客户没有考虑到的异常,那么是不是破坏了他们的代码因为他们写代码的时候却没有期望这个异常?

Anders Hejlsberg:没有,因为许多情况下,人们不介意。他们不捕捉这些异常。在他们的消息循环里通常有一个底层的异常捕捉。这里将弹出一个对话框告诉什么出错了然后继续。程序员通常写try..finally来保护他们的代码,如果异常发生,它将正常退出,表示他不对这些异常感兴趣。Throws语句,至少是java的实现,不必要地强求你捕捉异常,即使你不想捕捉它们,它也要你准确地知道哪些异常将会被抛出。它要求你或者捕捉他们,或者加入你的throw语句。为了这些,我们要做很多繁琐的事情,比如,在每一个方法里声明throws Exception,这真是愚弄人,使程序员写更多八股,毫无用处。

Bill Venners:所以你以为通常情况下调用者不会明显地捕捉这些异常,而不是在调用链上用catch去捕捉

Anders Hejlsberg:奇怪的是人们认为重要的事是去捕捉异常,这不是异常的关键

在一个写的好的程序里,我的看法,try..finally try..catch应当是十比一的。在C#里,using 语句,有点像try…finally.

Bill Venners:那么在finally里做什么呢

Anders Hejlsberg:finally子句里,你使你的代码继续执行即使异常发生,但是你可以不去捕捉它们。错误处理放在其他地方。对像现代UI这样的事件驱动的应用程序,典型地把异常捕捉放在主消息循环里;异常到这里才被捕捉。在其他地方,你用try…finally确保释放你申请的资源,你自己清理这些东西,你总是保持一个一致的状态。你没有必要在100个地方捕捉异常,弹出错误对话框。

如果你要改变你弹出对话框的方式,那就恐怖了。异常处理是应该集中起来的。你只需保护你的代码,因为异常会传播到异常处理器。

Checked Exceptions的可升级性

Bill Venners: 那么checked exceptions的可升级性呢?

Anders Hejlsberg:可升级性多少有点和版本控制相关。简单地看,checked exceptions是诱人的。举例来说,你可以表明你已经捕捉了FileNotFoundException,这不是很好吗?是的,这很好,当你只调用一个API。但当你构建一个大系统,你要和5个不同的子系统交换,每个子系统抛出410个异常,现在,你把它们整合到一块,将有指数级的异常你得处理,你得声明40个你可能抛出的异常。

如果你还要和另一个子系统整合,会有80个异常你要声明,简直失控了。

大的方面,checked exceptions变得这样不合理,以致人们到处用throws Exception,还有,我可以告诉你我多少次看到这种情况,他们敲入‘try da da da da catch curly curly,说,‘哦,我等会回来处理这些空的catch“,显然,他们根本不会做。

这种情况,checked exceptions显然大大降低了系统的质量。

此外,就算这些问题都没有了,对于我,把checked exceptions这类东西放入C#只是意味着需要更多的东西。另一方面,知道什么异常会被抛出是有巨大意义的,已经有许多工具可以做这些了。我认为不必要定义一些硬性的规则显示编译错误等等。但是我们肯定做一些可疑代码的分析工具,包括未捕捉的异常,为你指出这些潜在的漏洞。

3.委托,组件,和简洁

评语:赞成,不过我还是希望C#开启这个功能,但是不能强制,像JAVA的ECLIPSE那样,就去死算了;我虽然JAVA项目做得不多,但是ECLIPSE那个东西用多了就出现了,逻辑上的悖论;

是真异常还是异常流程;

有些高手认为应该按照面向对象设计,但是我认为未来面向对象也会像面向编程发展,结果F#证明了,不要被面向对象麻痹了,各有优优缺点;

面向对象在设计逻辑性优点突出,但是很多情况下也会出现思想上的臃肿;至少我们的计算机语言本身是结构化的;结构化的是最有效率的,你的编译器如何翻译就成了问题;

给提示就行了,不要强制啊。

简单化和简洁性

Bill Venners: C#JAVA的一个不同的地方是如何传递事件给对象,JAVA用类,经常是内嵌类,实现listener接口;C#用委托,有点像函数指针,为什么?

Anders Hejlsberg: 我先讲一些关于简明性的的话题。没有人认为简单不好,但是理解确各有千秋。我喜欢称“简单“为”简洁“。当你面对一个非常复杂的东西,想把它变简单一些,你通常只是把复杂性包裹起来,并不实际构造一个真正的简单系统,说不好还使它变的更复杂,因为用户想知道一些你忽略的东西。

而简洁,对于我,是头等重要的,随着项目的发展,它会越清晰,而不会越来越复杂。

委托和接口

Anders Hejlsberg:委托提供了一种类和接口没有的表达能力,也就是我认为它重要的原因;领先的语言已经认识到它的重要性。

它有许多叫法:函数指针,成员函数指针。在LISP里,它是封闭的,是函数式编程的真谛所在。它非常有用。

Bill Venners:哪方面

Anders Hejlsberg:你确实能用接口做委托所能做的事情,但是你必须做一些繁琐的事情。看看在JAVA里如何处理事件,和.NET里的比较一下。

因为JAVA里没有委托,你只能用接口

你可以定义一个接口代表所有的事件,接口可能定义一个,两个,三个,四个更多的方法。这样就有一个问题,如何组织它们就很模糊了,对那些事件,你定义了多少个接口,是一个事件一个,还是全部事件共用一个。没有很清晰的准则,甚至有时两者都有。

为了处理组件的事件,你必须实现事件的接口,如果你要处理两个不同的组件发出的相同的事件,你要实现两次事件的接口,这是不允许的,实际上你需要一个代理

麻烦的事情就开始了。

Inner classes can help a bit with that housekeeping, but nonetheless, part of the problem with using interfaces is that the receiver of the event has to know that he is receiving the event. He has to explicitly implement the listener interface. With a delegate, by contrast, as long as the signatures are compatible, you can just slot them together. The guy handling the event doesn't care how he got called. It's just a method.

内嵌类对这种麻烦有一点帮助,虽然如此,还有一个问题是事件的接收者必须知道它是接收这个事件,它必须显式地实现侦听接口。而与此对照,委托,只要函数签名兼容,你就可以把它们放在一个事件槽里,处理事件的人不关心怎样调用,它只不过是一个方法。

Bruce Eckel:有点像类型弱化

Anders Hejlsberg: 是的,确实是

Bruce Eckel: 因此更灵活

Anders Hejlsberg:是的。它完全基于是否有兼容的签名,比如,参数列表。如果是,你就可以把它们捆在一起。它也完全符合最终用户对回调函数的期望,不是吗?给我一些参数,然后我将写一些代码。很像一个方法,不过我给你的是那个方法的引用,这就是所谓的委托。

Bruce Eckel:还有你没有损失类型检查,类型检查在运行时发生

Anders Hejlsberg:不,还是在编译时。当你构造一个委托,就像C++程序员调用一个成员函数指针。委托引用一个实际对象的方法。如果那个方法是虚拟的,你能实际知道将调用哪一个(通过VTBL)。所以某种意义上,你就可以在委托构造的时候决断虚拟方法,通过委托调用字面上就是一个间接调用指令。

Bruce Eckel: 没有别的间接性

Anders Hejlsberg: 对,你只要一次间接的对于VTBL的决断,然后每次对于调用这个委托就直接跳转到实际的方法。所以不仅委托比接口分发有效率,甚至比正规的方法调用有效率。

Bruce Eckel: C#还有一个多路委托,一个委托可以调用多个函数,这是一个正交的特征吗

Anders Hejlsberg:多路委托是一个完全正交的特征。说实话,对于多路委托是不是一个非常有意义的事情我是保持中立的。我认为它有它的用处,有些人发现多路委托很重要,也很好地利用了它。但是我也认为实际上很多情况下委托是单路的,实际上你不用付出多路调用的开销除非你用它。

 

Bill Venners:委托是如何符合你先前说的关于简单和简洁的说法的,哪里简单,哪里简洁?

Anders Hejlsberg:如果你用接口来仿效委托,就会有一大堆诸如代理等琐事。事实上,看那些整合JavaBean的工具,产生一堆代理,然后说,“不要动下面的代码,我们为你隐藏这些小的帮助类”,这对我而言就是简单。系统没有真正地容易了,实际上是复杂了,但是你没有仔细看而已。

 

注释:面向接口不是万能药,我认为都统称为契约更加合适;而你的契约是引用的还是非引用的,都各有利弊;

组件是第一类的

 

Bill Venners:

发表在O'Reilly Network上的一篇采访中,关于C#为什么支持属性和事件,你说,“开发者是在构建软件组件,而不是构建单一的应用程序或者单一的类库。每个人都在构建从宿主环境中继承的组件。这些组件重载一些方法和属性,捕捉一些事件,然后隐藏这些组件。把这种观念当作第一类的是很关键的。”

我想更好地理解你的意思,因为我趋向认为我是在写一些类,而不是组件,你是在谈那些用Delphi,VB,JavaBean写的用来互连的组件吗?这是你理解的组件吗?

Anders Hejlsberg:对于组件,主要的是你能够拿起来就用;听起来很不错,但是我通常不是这样理解的。最简单的形式,组件就是一个类加上一些其他东西。组件是一个自包含的软件单元,不仅仅是代码和数据。

它是一个通过属性,方法,和事件显露自己的类。是一个有附加属性如元数据和名字模式等东西的类。这些属性提供关于如何在宿主环境下使用组件的动态信息,如何持久自身-所有这些附加的与元数据有关的东西。元数据可以让IDE智能感知组件做什么,显示它的文档。组件把这些都包装起来。

Bill Venners:当我使用JAVA的时候,我感觉我是在设计类库而不是组件库。也许是因为getset太麻烦。我确实用getset,我也确实发出事件,但是我并没有想它们用在一个构建BeanIDE。我想它们是给一些写代码的人用的。所以我仍然想知道多少人实际在写javaBean式的组件,如果这是趋势的话。因为以我的经验来看并不多。

Anders Hejlsberg:当代主流面向对象语言都是混合体。有许多结构化编程思想在里面。对象很多只是一些方法和一个this指针的构成的。概念上地理解,当你想象一个对象或者组件时,它们有属性和事件。

以第一类元素对待这些东西将使事情简单。

人们争论C#对属性和事件的支持只不过使语法滑头。但是难道不是所有的都是语法滑头吗?对象就是吗,我们玩转VTBLs,那个也能用c宏做,你也可以用c写面向对象程序,只不过有点复杂,同样地,你可以用c++java写组件,可是因为一些核心概念在这些语言中不是第一位的,你只能走弯路。

还是那句话,属性不是真正的属性,它们使getBlasetBla,但是在属性查看器里却是bla,你只知道有这个对应。很清楚组件是趋势。因为组件的概念不是第一位的,在众多语言里我们像用类一样使用组件。这是不应该的,它们应该改变。

我们已经谈论PME编程模型-属性,方法,事件很久了,日常我们也用它,为什么不把它提升到第一位呢?

 

版本,虚拟,和重载

非虚拟是默认的

Bill Venners:JAVA中,实例方法默认是虚拟的,可以被子类重载,除非显式声明final。而在C#中,实例方法却是默认非虚拟的。程序员要显式声明它为虚拟。为什么C#默认非虚拟呢?

Anders Hejlsberg:有几个原因。一个是性能。我们注意到当人们写JAVA代码的时候,忘记标识方法为final,因此是虚拟的,所以它们有性能上的损失。一个虚拟方法是有性能上的负担的。这是一个问题,更重要的问题是版本

关于虚拟方法,有两派理论。学院派认为“任何东西都应该虚拟,因为将来某一天我要重载它”,那些来自在真实的世界里写真实的程序的实践派认为,“我们真的应该对虚拟化小心”

当我们让一个平台上的一些东西虚拟化,我们就对它将来的演化做了可怕的承诺。对于非虚拟方法,我们承诺调用它的时候,xy会发生。而对虚拟方法,不仅如此,我们还要承诺,当你重载它,我们将在一个其他方法的序列中调用它,而状态是不定的。

你使一个API虚拟化,其实你是在建立一个回调钩子。作为OS或框架API的设计者,你真的需要很关心这些东西。你不想用户在任何地方重载和挂钩这个API,因为你不能作出你的承诺。人们也可能不能完全理解它们所作的承诺当他们使一些东西虚拟化

 

引入和引出契约

Bill Venners:

听起来你并不在意调用者没有正确地实现你对他们的承诺;你担心的是如何对重载作出合适的承诺?

Anders Hejlsberg:

我两者都关心。实际上,虚拟化有两个方面:引入和引出。人们通常只在意引入的契约,而忽视引出的契约。

Bill Venners:引入和引出契约是什么意思

 

 

Anders Hejlsberg:引入的契约就是当你调用一个方法的时候你要做一些什么。它指示我当我调用一个方法的时候,我要先满足一些要求和方法返回时发生什么。引出的契约就是当你重载一个方法的时候你要做一些什么;如今大多的API在提供如何重载的文档都做的不好,调用方法的不变式是什么?那些调用完还要保持为真,那些方法你是不能调用的等等。我认为默认地每个方法都要有引入和引出契约是很危险的。相信每个人都能写好如何重载的文档和重载的不变式也是很危险的。

我可以举一个真实的版本问题:我们确实从JAVA中看到的。无论什么时候我们发行一个类库的新版本,破坏就发生。只要我们在基类里引进一个新的方法,如果派生类里有个名字相同的方法,那么它被重载了,特别的是如果有不同的返回类型,程序就不能编译了。问题就在于JAVA, C++没有真正了解虚拟的实际意义。

 

注释:就是就是;同上,就是这个思想;

虚拟有两种意思

Anders Hejlsberg:

当你声明virtual,有两种可能的意思。如果你没有覆盖一个相同签名的方法,那么是一个新的虚拟方法。或者重载一个继承的方法。

以版本观点看,程序员指示出他们使一个方法虚拟的实际意图是很重要的。在C#里,你必须显式地指明你的意图。定义一个新的虚拟方法,你用virtual,如果重载一个存在的虚拟方法,你得用override

因此,C#就没有这样的版本问题。我先前描述的在基类里引进一个已经在派生类存在的方法的例子。在派生类里,你已经定义foo为虚拟。现在我们引进一个新的虚拟函数foo,那么我们现在有两个虚拟的foo(而不是被重载),有两个VTBL槽。派生类的foo隐藏了基类的foo,这没有问题,基类的foo在派生类定义foo的时候还不存在。因此覆盖它(就像普通函数一样被覆盖)没有任何问题,一切还安然无恙。

Bruce Eckel:

你看到这种版本问题如此经常发生,所以你决定解决它?,我记得载DELPHI中你做了类似的事情.

Anders Hejlsberg:

是的

Bruce Eckel:.

你对语言的态度和我谈过的其他人有点不同,你是非常实际的

Anders Hejlsberg:我是个一个实用主义者,很有意思,版本控制已经是语言设计的一个重要支柱,就像C#在重载虚拟方法所做的。还有,对C#的重要决议也是和其他语言不同的,因为版本控制,当我们开发一项新特征,我们总是要对版本控制进行重复检查。我们会问,“版本控制会怎样改变这个,如何以版本控制的角度看待这个问题?”这是对大多的语言设计的一个重要转变,他们通常很少顾及这些。

Bruce Eckel:你关心版本控制主要是因为该死的DLL问题?

 

Anders Hejlsberg:有点,主要是由于这几年我的观察。1015年以前,回到640K限制的时代,你只能丢掉代码重新写。重写花了一年,那样与下一次发行相符。所以你没必要担心旧代码的重用。版本控制,什么东西,我们每次都从头开始?

那已经过去。我们不能追上摩尔定律的速度。我们获得更多功能的方法是平衡已经存在的基础架构和应用程序。系统的生命周期长了,版本控制就变得重要了

 

3.Contracts and Interoperability

 契约和合作

DLL的地狱和契约理论

 

 

 

 

 

Bill Venners:实际中“DLL HELL 给接口使用带了什么程度的问题呢?如果每个人都理解并坚持dll中函数的契约,而且不更新dll使不破坏任何代码?

Anders Hejlsberg:地狱有很多苦痛。一个方面是你没有坚持你承诺的语义契约。你做了一些和你先前做的不一样的事情,因此破坏了代码。可是这并非是最大的问题。真正的问题是不允许你有多个不同版本的dll并存。你升级一个就升级了所有。这才是大问题.

Bill Venners:但是如果遵从契约,是不是所有的用户都使用最新的dll了吗?

Anders Hejlsberg:理论上,是的。但是任何改变潜在的是一个破坏的改变。甚至一个错误更正也能破坏代码如果有人依赖这个错误。严格地说,一旦你发布了,你就不能做任何事情了。

版本控制就是以正确的方式放宽这些限制,带来回旋的余地。绝对地,为了不破坏任何代码,就是不要改变任何东西。因此支持多版本的在一个域或进程里并行执行就很重要了。所以在.NET中我们选择并行执行而不是旧的DLL模型。

Strong Names in .NET

.NET中的强命名.

Bill Venners:我想你是用强命名来标识你要的库的版本的。强命名是如何工作的呢?

Anders Hejlsberg:强命名有逻辑的和物理的两部分。逻辑部分由名字空间和类名组成。实际上,从公共语言运行库(CLR)看,名字空间是不存在的。我们可以在编程语言里实现名字空间,但是在CLR里,类名是可以包含”.”的。这就是类的逻辑名。物理名包含在包含代码的程序集的名字,版本号码,区域,还有一个名字的加密键。你可以通过一个有特定版本号或强名字键的特定程序集引用一个特定的类型。你可以得到保证你可以正好得到你想要的实现。或者,你可以放松强名字的限制,你可以说“我只要这个版本以后的”

强制语义契约

Bruce Eckel:你对强制语义契约有什么想法吗

Anders Hejlsberg:

我认为最有前途的思想已经存在了。最新的发展如前后条件,断言,不变式等。微软研究院已经有几个正在进行的项目我们正在不断地评估。我们已经看过几份很漂亮的具体的提议书。最后我们意识到-像对其他所有重要的特征-你不能从纯语言层面实施强制契约。你得把它放入基础架构,CLR和公共语言标准,因而所有其他得语言得益。如果包含不变式对于接口的实现者只是可选的话,不变式还有什么意义呢?所以把它放在类型系统层面比放在程序语言层面好;但是并不表示我们没有去理会它。下一个版本我们不会做,但是以后肯定是C#的第一类特征。

互通性

Bill Venners:

你曾经说JAVA是为了争取平台的独立性,而.NET是在争取互用性。互用性是什么意思?

Anders Hejlsberg:

互用性意味着几样东西。首先,意味着在.NET中不同的语言的互用性。我们的设计目标就是.NET CLR将与语言无关,可以支持多种语言。而JAVA,只是一门编程语言。CLR支持只有少数语言有的特征,比如,指针,C#中不安全代码,托管的C++有,但是VBJscript没有。然而指针的基础却是由CLR支持的.

语言的互通性是基于这样的意识:我们不会试图说服人们都用一种语言,实际上新的语言的诞生将促进工业的前进和思想的革新,我们不会阻拦它相反我们鼓励它。互通性的另一个方面是和已有的系统的互通性。当谈到JAVA,因为我们认识到互通性如此关键,所以认为JAVA VM急待提高的就是和已有的代码的互通性。因此在CLR中,我们对和DLLCOM,OLE所有在.NET之前写的代码的互通性极度关注。

这回到我先前说的现如今关键的是平衡。我们要为程序员寻找一种与已有代码平衡的方法。达到这个目标就意味着伟大的互通性,因为平衡是如此困难。这就与JAVA宣称的”100%纯JAVA”的观点相反。我认为我们做的是正确的。

你对比一下在C#中和在JAVA调用DLL,很容易就明白我的意思。JNI太复杂了,而且我认为就让它这样而不做更多更好的事情是非常可耻的。为了和别的系统互通而不得不进入甚至比直接用CC++更复杂的黑暗世界,有什么意义呢?你得运行一些工具分离一些头文件,你得记住调用了这个和那个。如果你要释放一个对象,你得上锁,当GC恰好醒来,你就不能复制它了。仅做一些没有道理的事情。

Bill Venners:我以外人的身份来看Microsoft Sun,我注意到它们之间不同的文化或哲学影响了.NETJAVA的设计.Microsoft 我看到主流的观点是软件是用来对硬件-计算机,设备等编程的,我认为也是微软如何赚钱的一个整体合理的看法。作为对比,虽然“网络就是计算机”是Sun的市场口号,我发现在SUN的文化中却是没有这种态度的。网络提供服务,而不是计算机,JAVA就像是网络协议栈顶部的面向对象层,目标就是抽象出异质的网络连接。

Anders Hejlsberg:有趣的是真实世界中的JAVA解决方案都有一些平台特定的东西在里面。我没看到一个是纯JAVA的,或多或少依赖一些其他组件.任何WEB有关的都依赖于Apache或其他WEB服务器,数据库,和其他形式的与这些系统的互通性。认为整个世界是纯JAVA是很可笑的.事实上,世界就是融合所有的系统然后使它们工作,也就是为什么有诸如WEB SERVICES这样精彩的东西.因为它提供一个很好的互通的解决方案。我们互通性是来自很多方面的,我们要做的就是努力地使系统互通变得更好更容易。

Bill Venners:你刚才所描述的就是我在Artima.com所做的,我用Tomcat运行JSPTomcat再和Apache沟通。

Anders Hejlsberg:也许还有后台数据库

Bill Venners:是的,JAVA应用程序连接数据库,有一些是用JNI实现的平台相关的东西。但是在我的应用程序全部是JAVA,甚至Tomcat Apache和数据库的连接器都是纯JAVA,因为它们是用SOCKET,不是JNI

Bruce Eckel:我喜欢Python的原因之一就是它宣称如果你想平台无关你可以,如果你想与平台通讯你也可以

Anders Hejlsberg:是的

 

4.Inappropriate Abstractions

不恰当的抽象

Loosely-Coupled Distributed Systems

松耦合的分布式系统

Bill Venners:O'Reilly的采访中,你说,“当我们第一次考虑.NET框架的设计的时候,我们回顾了一下WEB中实际在发生些什么,发现它变成了一个松连接,非常分布的世界。我们试图发现它将怎样影响我们的编程模型。所以我们的设计开始于假设分布式的程序是建立在松连接,无状态的形式给你带来非常高的可调节性,你只要很轻松的在各处部署一下就可以了。一旦作了这个基础的假设,一切就变了”,什么改变了?

Anders Hejlsberg:

510年前,主流的思想认为如何构建分布式系统的趋势是CORBA,IIOP,对象请求代理。那时候的观点就是要使世界变得像对象化,实际地,建立一堆基础架构来屏蔽对象的分布.终极理想就是你只要写Object obj = CreateMeAnObject() 然后调用obj.ThisMethod(),obje.ThatMethod(),即使你不知道那对象是在泰国,隔壁,或者在同一进程。

这种编程模型的问题是:在单个进程里运行很好,进程间也不错,在小的局域网里还可以,但是再扩大就很有问题了。

如果你隐藏消息穿越网络这个事实,并且不知道何时穿越,你就死在繁琐的握手通讯。所有的意外,传输速度对于你都是问题。你不可以介入一个部署再纽约的对象的交换,你不能obj.LetMeGetX(),obj.LetMeGetY(),obj.LetMeGetZ(),你应该说obj.LetMeGetXYAndZ(),然后所有东西一块返回;但是你不必要这样做,除非你真的让人们了解它们是在构建分布式系统。换句话说,你不应该试图以本地对象包装远程对象,因为它们是不同的。Web services就区别了它们。

还有,web services运行在一个已经知道如何度量的基层框架中,web services运行于HTTP上,就是每天我们打开浏览器是所做的机器到机器的通讯。我们很清楚知道如何衡量它。为什么不用它来平衡呢。这就是web services所做的。而相反,我们却很难衡量CORBA系统,对它没有可靠的知识积累,我还没听说过有人实际成功地做过这个。

The Stateless Fashion

Bill Venners:你说,“分布式应用程序建立在一个松散连接,无状态的..” 你说的无状态是什么意思?

Anders Hejlsberg:如果你对远程对象编程,无论何时你实例化一个新的对象,最终你会保持一个在同一电脑的内存里或分布系统的其他地方实例化的对象的一个代理,只要你保持这个引用,远程对象也就保持活动状态。你会进入一个长期活动的事务,这种情况是有问题的,因为这样的分布系统很难。一旦你撤走那个主机,如果还有调用进来,你得放回去。

HTTP就不一样,你被强制依赖于HTTP的无状态,系统不保持通道的状态,你设计的系统被强制对每个请求自由地路由到相应的CPU,然后返回结果。下一次又可以在别的地方。

Bill Venners:

服务器没有必要是无状态的,因为状态被保存了

Anders Hejlsberg:无状态使得分布机制不用保持活动连接

Bill Venners:我不太理解有什么不同,两者都是有状态的啊,在分布系统中没有状态的优点是什么

Anders Hejlsberg:有意思。某种意义上,web service帮你实例化对象,保持它,调用它的方法。Web service 仅仅是一个进入点,状态是你传进来的,然后把结果状态传会给你,接着就没有记忆了。它没有长久保持活动的对象,它没有对话期

Bill Venners:

这个协议里,没有用对话期

Anders Hejlsberg:

是的

Bill Venners

但是通常服务器都会产生一个对话

Anders Hejlsberg:当然,但是如何设计对话,我们没有告诉你一定要像实现这样一种对话观念:客户端实例一个新的对象,只要保持它,服务器端就得保持它得状态。你最好了解如何使它们工作。

Object-Relational Mappings

对象关系映射

Bruce Eckel:.NET框架是如何支持对象持久的呢?

Anders Hejlsberg: 没有一个适合任何人的对象持久方法。有时候,你要通过电线把对象发送到其他的线程 ,这样你仅需要把它以数位序列化,而不需要关系版本问题。对于哪些长久的存储,你就可能要更好地考虑版本了。在20的时候读10写的对象,这种情况你得写一些表示转换来解决版本问题。有时候你需要把对象存储在数据库里查询它,这种情况,你真正需要的是对象关系映射。在.NET中我们有一个实现,并且正持续演化。

Bruce Eckel: 对象关系映射的主要问题是什么?

Anders Hejlsberg:

对象关系映射生死攸关的问题是缓存机制是否足够灵活。遗憾的是大多都不是。在.NET中我们非常努力使缓存机制完全受控于你自己,或者更本就不用。很多情况下,你发出一个查询,然后获得以对象返回的结果,但是你不想框架把它缓存起来以便你可以再一次获得同样的对象。很多系统只能这样做。结果是可怕的性能负担,而你根本不需要这些的。在中间层,比如,你并不在乎缓存,因为你仅仅服务一些很快就结束的HTTP请求,为什么要缓存呢?

Bruce Eckel:所以缓存应该是被请求的,而不是默认提供的

Anders Hejlsberg:是的,大多的对象关系映射都有缓存和引用一致方面的问题。如果你请求一个客户对象,下一次你再请求就返回已经保存的相同的对象。这样就需要一个很大的包含所有你已经看过的东西的哈希表。

Bill Venners:

为什么我要介意是否是相同的对象呢

Anders Hejlsberg:

假如你获得的CustomercustID100.在一个面向对象的程序中,在查询中获得那个Customer,如果你再次请求,你希望得到的是什么呢

Bill Venners:

一个与我先前获得的语义相同的对象

Anders Hejlsberg:你希望获得的是相同的对象引用吗?

Bill Venners:

只要语义上相同,我才不在乎这个

Anders Hejlsberg:真的?因为这个对你的程序运行有深刻的不同。你是认为customer作为一个对象,仅仅只是一个,或者,只是数据库中的一份拷贝?大多对象关系映射都认为Customer就是一个custID100的对象,如果你获得一个Customer,设置了一些属性;如果你只改变了这份拷贝,而不是那份,如果两个人同时更新对象的两份拷贝,谁先更新呢?

Bruce Eckel:对于这些困惑最好保持透明。

Anders Hejlsberg:让我想起早期我们关于CORBA的讨论,试图隐藏一个应用程序的分布性。这也一样。你要隐藏数据是在数据库里的事实。你可以做到但是是有代价的。

 

Bruce Eckel: CORBA是试图隐藏网络。而JINI,他们说“不,网络是存在的,在某中层面上我们必需知道它,除非事情变得非常复杂”,设计的窍门就是在哪里作出确认:这是我们必须主意的边界。

我认为这种问题是存在于对象关系映射中的,有意义的是领会到什么是正确的抽象。

Eric Gunnerson:

问题是:你真的需要抽象吗?通常情况下你并不需要。在当前的.NET远程调用的实现中,有一些相似的透明性,很多人说,“是,我知道我在远程调用,我知道对象在那里,不要白费力气使它像本地的一样”

Bruce Eckel:

有时候你会发现如果你试图做像本地-远程透明的抽象,突然就变得不可理喻的复杂。然而如果你意识到‘我在这里调用它,网络可能会失败,我必需知道“,这样事情才会明朗起来。对于对象型数据库,好像也有类似的情况。我必需得接受同样的Customer对象可能有多种表示,可能我得通知对象我已经完成,可能得有一个事务处理。

Anders Hejlsberg:这样就更好,因为当用户对可能发生得事情深入考虑的时候,作为设计者,你要尽力给用户更全面的能力

Bruce Eckel:所以你要在正确的层次上抽象,而不因为错误的抽象给用户带来那么多问题。

Eric Gunnerson:错误的抽象造成的麻烦就是除了它没有其他出路。类的设计者很难对使用情景做出合理的猜测,更不用说相关的使用频率。你可能认为用户需要透明性,因为可以让他们做一些很酷的事情,所以你实现了透明性。但是如果99%的用户变得不再用它,猜猜出现什么情况,这些人为这些不用的东西付出了代价。

Dan Fernandez:另外一个问题就是很多开发者对一切东西都想用一样的方法。人们会说,“好,有一个对象关系映射,我们的程序里所有东西都将使用它”,这一些情况下是有用的,但是其它像股票交易系统一样的东西,你更本就不想有一个固定的持久层。但是你因为它是个解决问题的办法而使用它。 对象关系管理也许是正确的解决方案,但是有时人们却一概而论地以为对任何东西都是正确的,这真是很有害的。

Bruce Eckel:

但是你知道为什么,是不是?原因是现在我只要学一种持久模型,到处可以用它。

Dan Fernandez:

Bruce Eckel:可能答案是构建一些接口,实现取决于怎样使用它,这样我就只要学习一个接口,然后根据调用的方法自己或者由系统选择一个实现。

Eric Gunnerson:当然,接口也是一种抽象

5.Generics in C#, Java, and C++

C#Java,C++中的模板

Generics in General

一般的模板

Bruce Eckel:你能简单介绍一下模板吗

Anders Hejlsberg:.

模板本质上就是一种可以给类型加上参数的能力。也称作参数化类型或参数多态。经典的例子就是List集合类.List是一种很方便增长的阵列。它有排序方法,可以搜索等等。如果没有参数化类型,你会犹豫到底是直接使用数组还是用List。如果使用数组,你获得了强类型,因为你可以定义一个Customer的数组,但是你失去了可增长性和一些可用的方法。如果你用List,你得到所有这些方便,但是你失去了强类型。你不可以说LIST是什么的LISTLIST就是OBJECTLIST。类型在运行时检查,通过接口也表示在编译时不作检查。

 

编译器会允许从一个装有CUSTOMERLIST中取出一个String.你只能到运行出错的时候发现它。还有,如果你把简单类型放进LIST,你得装箱.因为这些麻烦所以你才会有顾虑,你通常很难决定到底选用哪一个。模板带来的好处就是你能真正装进你要装的东西。因为你能定义List<T>,当你使用LIST的时候,你能说是个什么的LIST,让编译器为你做类型检查。直接的好处带来了一系列的好处。不仅对LIST,哈希表,字典――映射键到值的集合,你可以映射StringSCustomerS,或者intSOrderS,这都是强类型形式的。

Generics in C#

C#中的模板

Bill Venners: C#中的模板是如何工作的

Anders Hejlsberg:

没有模板的C#中,你只能定义class List {….},而有模板的,你就可以定义class List<T>{…..}T是一个类型参数,在类的定义中你可以把T当类型使用。当你要创建一个List对象时,你可以用List<int>List<Customer>.你从List<T>构造了一个新的类型,如果你用实际类型取代类型参数。所有的T变成了intSCustomers,你不需要向下转换,而且任何地方都要强类型检查。

CLR中,当你编译LIST<T>或者其它模板类型,它像其他普通类型一样编译成IL(中间语言)和元数据。IL和元数据包含一些附加的信息知道这是一个类型参数。当然,原则上,模板类型和其他类型是一样编译的。在运行时,当你的应用程序第一次引用LIST<INT>,系统检查其他人是否已经引用过LIST<INT>,如果没有,它读入LIST<T>IL,元数据和类型参数int,及时编译器JITer,取代类型参数获得LIST<INT>

Bruce Eckel:

因此它是在运行时实例化的

Anders Hejlsberg:是在运行时实例的,它在需要这个类型的时候产生代码,字面上理解,你定义一个LIST<INT>,你就获得一个int的列表.如果在模板代码中有一个T的数组,就变成了一个int的数组。

Bruce Eckel:

  垃圾收集器也在那里收集吗

Anders Hejlsberg:可能是也可能不是,但是这是一个正交的问题。类在程序域构建,在程序域里永远存在。如果程序域退出,类也消失,像其他的类一样。

Bruce Eckel:

如果在我的程序里用到了List<int>List<Cat>,但是没有执行到用List<Cat>的分支..

Anders Hejlsberg:

那样系统就不会实例化List<Cat>,现在有一个例外,如果你在操作一副图像,如果你预先产生一副本地图像,你可以早点实例化。但是在普通情况下,实例化是需求驱动的,延期到尽可能晚。当你对像List<int>,List<long>,List<double>,List<float>等这些值类型实例化,我们为它们分别创建一副代码拷贝,所以List<int>有自己的代码,List<long>有自己的代码,List<float>有自己的代码。但对所有的引用类型则共享代码,因为它们的表示是一样的,都是指针。

Bruce Eckel:你只需要转换

Anders Hejlsberg:不,实际上没有,我们可以共享本地映象,但是它们有不同的Vtables。我只是表明我们在有意义的情况下才共享代码,而当涉及性能的时候我们却不。典型的如值类型,我们非常在意List<int>就是int,你不想把它们装箱,尽管可以这么做,但是是很昂贵的方法。

Bill Venners:在引用的情况下,你实际是有不同的类的,List<Elephant>是和List<Orangutan>不同的,但是它们共享相同的方法代码,是吗

Anders Hejlsberg:是的,作为实现的细节,它们实际共享本地代码

Comparing C# and Java Generics

比较C#Java的模板

Bruce Eckel:

比较一下C#和Java的模板?

Anders Hejlsberg: Java的模板实现是基于一个叫Pizza的项目,由Martin Odersky和其他一些人完成的,Pizza已经更名为GJ,转到JSR,最后加入JAVA语言。它的关键设计目标就是在不改变VM的情况下实现模板。当然这是很有意义的,但是由次也带来了一系列的局限性,开始并不明显,但是渐渐地,你就会发现“哦,怎么这么奇怪”

比如,在Java的模板中,你不能获得任何我刚才讲的执行的效率,因为当你用Java编译一个模板类,编译器只是去掉类型参数而用Object代替。所以最后的编译结果和你当初用Object的是一样的。当然你如果用List<int>,你得把int装箱。所以这里就有负担。此外为了通过VM,编译器实际插入你没有写的类型转换。如果你要把一个装有ObjectListCustomerS对待,你得把它强制转换成Customer。在它们的实现中就是自动为你插入这些强制转换。所以你获得了语法上的方便,但是你没有获得任何执行上的效率。这就是我认为的问题一。二

我认为可能是一个更大的问题,因为JAVA的模板实现仅仅就是去掉类型参数,在运行时,你不能得到和编译时一致的表示。当你对List的模板做反射的时候,你不能知道List是什么List,仅仅知道是一个List。因为丢失类型信息,任何动态代码产生和基于反射的情况都不能工作。对我来说,类型信息越详细越好。而在我们的实现中,所有的信息都是可用的。你能通过反射获得List<T>System.Type。你不能直接创建List<T>,因为你不知道T,但是如果你通过反射得到intSystem.Type,你可以通过反射把两者结合在一切来构建List<int>,而获得另外一个List<int>System.Type.所以典型地,任何你能在编译时做的事情都可以搬到运行时做。

Comparing C# Generics to C++ Templates

Bruce Eckel:

比较一下C#和C++的模板?

Anders Hejlsberg:

对于C#C++模板的区别的最好的理解是:C#的模板很像类,除了有一个类型参数。C++的模板像宏,除了看起来像类。最大的不同在于何时类型检查和怎样实例化。首先,C#在运行时实例化,C++在编译时或者链接的时候。无论如何,C++是在程序运行前实例化的。这是第一不同点。第二C#在编译模板类型时做强类型检查的。对于不受限制的List<T>一样的类型参数,可用的方法就是根类Object所有的,因为这是我们唯一能保证存在的。所以在C#中,我们保证任何对参数类型的操作都会成功。

C++则相反。你能对参数类型的变量做任何事情。但当你实例化它,可能就不能没用,就会出现一些隐秘的错误信息。比如,有一个参数类型T,和T的变量x,y,你把两者相加。那么你得有定义operator+才行,否则就会得到一些隐秘得错误信息。所以某种意义上讲,C++的模板实际上是无类型的或者说是松类型的。而C#则是强类型的。

Constraints in C# Generics

Bruce Eckel:

C#的模板的约束是怎么工作的

Anders Hejlsberg: C#的模板中,我们可以对类型参数加上约束。比如List<T>,我们可以说class List<T> where T:Icomparable.意思是T必须实现Icomparable.

Bruce Eckel:

C++中,约束是暗含的

Anders Hejlsberg:是的,在C#中我们当然也可以做同样的事情。假如Dictionary<K,V>有一个方法add(key,v)的方法,这个方法就需要把传进来的键与字典里的键比较,那么就要用一个叫Icomparable。一个方法是把key参数转换成Icomparable然后调用compareTo方法。这样你就给key参数加上了约束。如果传进来的参数没有实现Icomparable,就会出现运行时错误,但是没有明显的规定要求你必须实现IComparable。当然你还的付出运行时类型转换的代价,因为你实际做的就是运行时动态类型检查。

 

有了约束,你可以把动态检查提升到编译时或者加载的时候。当你说k必须实现Icomparable一些事情发生了。对于任何K的值,你可以直接调用接口的方法,因为语法上保证了它已经实现了接口,当你构建一个类型的实例,编译器会去检查是否实现Icomparable,否则产生编译错误,或者如果是反射就产生异常。

Bruce Eckel:

你说编译器和运行时

Anders Hejlsberg:编译器执行静态检查,但是你同样可以在运行时通过反射进行检查。如我先前说的,在编译时候可以做的事情,同样可以在运行时通过反射做到。

Bruce Eckel:我可以定义一个带未知类型参数的模板函数C#可以对容器加入强类型检查,但能做到和C++中一样的这种类型弱化吗?比如,我可以不可以写一个对A a B a相加的函数?我其实只要有一个可以对AB相加的算子operator+而不在意实际的类型,因为这是一种类型弱化。

Anders Hejlsberg:

你真正问的是:你可以怎样使用约束?约束,和其它特征一样,如果你推向极端就会变得极度复杂。当你认为约束是一种模式匹配机制,你就会说,‘这个类型参数有一个带两个参数的构造函数,实现了operator+,有一个静态方法,两个实例方法,等等“,问题是,你想让这种模式匹配复杂到什么程度?

从什么都不做到完全模式匹配是连续的,我们认为什么都不做太少了,而完全匹配却太复杂了,所以我们做个折中。我们允许你对单独的类,一个或多个接口指定约束,和一个所谓构造器约束。你可以说,‘类型必须实现IfooIbar‘或’类型必需从X继承‘;对于这些,我们将在编译时和运行时进行检查。约束所暗含的方法对于类型是直接可用的。C#的算子是静态成员,所以它不能为接口成员,因此接口约束不能应用到operator+.唯一的方法是指明必需从一个有operator+的如Number继承。但是你不能直接说,“一定要有一个operator+”。所以最终我们还是迂回地解决了这个问题。

Bill Venners:

约束是对类型而不是对签名的。

Anders Hejlsberg:

是的

Bill Venners:

所以类型必需或者继承一个类型或者实现接口。

Anders Hejlsberg:是的,我们确实可以做的更多,但是就变得很复杂。增加的复杂性没有带来应有的收益。如果有约束系统不能直接做的,你可以用工厂模式做。例如,你可以定义一个Matrix<T>,Matrix中你可以定义一个产品方法。当然你最后要理解如何对T做乘法,但是这不是一个约束,至少T不仅仅是int,double,float。你能做的是使Matrix带一个叫Calculator<T>的参数,在这个参数中,有一个multipy的方法,你可以在其中实现乘法,然后传到Matrix中。

Bruce Eckel: Calculator也是一个参数类型

Anders Hejlsberg:

是的,这是一种工厂模式,有很多方法实现这些事情。也许并不是你认为最好的,但是却是相当简洁的。

Bruce Eckel:

我开始意识到C++的模板是一种类型弱化的机制。如果你在这上面加上约束,你就从弱类型像强类型转变了。而强类型往往带来更多复杂性。这真是一把双面刃。

Anders Hejlsberg:你意识到类型是一个转盘,你把它放得越高,程序员的生活就越痛苦,但是程序变得更安全,反之也是成立的。

6.CLR Design Choices

CLR设计的选择

Interpreting and Adaptive Optimizations

解释和适应性优化

Bill Venners:

Java字节码和IL(中间语言)的一个不同点是Java字节码的指令中有类型信息,而IL没有。比如,JAVA有几个加法指令:iadd,ladd,fadd,dadd分别对int,long,float,double进行加法;而ILadd直接加两个数,add.ovf加两个数,发出溢出有符号异常,add.ovf.un加两个数,发出无符号异常。所有这些指令从堆栈中弹出两个数,相加,然后返回结果。而java却根据堆栈中的数的类型采用不同的指令。作为对比,CLRadd指令好像是多态的:它对堆栈上的两个数相加,不管它们的类型,虽然对有无符号发出不同的异常。基本上运行IL的引擎要追踪堆栈上的值的类型,以便遇到add时,知道如何执行相加。我听说微软决定IL将总是被编译了,而不是解释执行。怎样在指令中嵌入类型信息帮组解释器更有效率地执行呢?

Anders Hejlsberg:如果解释器不需要追踪堆栈而直接执行指令,那会快一点。比如,iadd,解释器不需要计算要相加的是什么,它知道是整数。假如堆栈已经通过验证,很安全地节省了一些时间,而这些对解释器是很关键的。而我们的情况,我们没有打算让CLR解释执行。我们意图是JIT即时编译,而JIT是无论如何是需要追踪这些类型信息的,因为我们已经有这些类型信息了所以我们没有必要在指令中加入类型信息。

阅读更多
个人分类: 项目管理
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭