首页 > 开发 > 综合 > 正文

C#首席设计师Anders Hejlsberg专访(1)转

2024-07-21 02:22:21
字体:
来源:转载
供稿:网友
注册会员,创建你的web开发资料库,作者:john osborn

译者:荣 耀

[译序:精彩技术,不容错过!限于时间和能力,译文倘有讹误,当以英文原版为准。]



7月,o’reilly 编辑john osborn参加了微软职业开发者会议。在此,他对著名的工程师、微软.net框架设计师、c#程序语言首席设计师anders hejlsberg进行了采访。anders hejlsberg因设计pc上最早的语言之一—turbo pascal而出名。他把turbo pascal授权给borland公司,后又率队开发了delphi—一个极为成功的可视化的客户/服务应用设计工具[译注:此处不必拿midas之类较真j]。访问时在座的还有微软c#产品经理tony goodhew和o'reilly的windows编辑ron petrusha。

osborn:

     我已经看到一些关于c#[发音为"see sharp"]的新闻故事,我注意到有很多似乎倾向于这样的观点—或理论上说—c#不是java的克隆就是java的微软替代物。如果你来写这个标题,你希望人们怎么评论这门语言?

hejlsberg:

     首先,c#不是java的克隆。在设计c#期间,我们考察了很多种语言,如c++、java、modula 2、c、smalltalk等。很多语言都有我们感兴趣的相同的核心思想,比如深度面向对象、简化对象等等。

     c#和这些别的语言尤其是java的关键不同点是它非常接近c++。在我们的设计中努力使然。c#从c++直接借用了大多数的操作符、关键字和声明。我们还保留了许多被java抛弃的语言特性。为什么java中没有枚举,道理何在?我的意思是,抛弃它们是基于何种理论基础?在c++中,枚举显然是一个很有意义的概念。在c#中,我们保留了枚举并同样使其类型安全。并且,枚举不只是整型,它们实际上是从.net基类库里的system.enum派生下来的强类型的值类型。如果没有造型转换,枚举类型“foo”和枚举类型“bar”不可互换。我认为这是个重要的差异。我们还保留了操作符重载和类型转换。c#名字空间的整体结构也非常接近c++。

     但是,超越这些传统的语言论题,我们设计语言的一个关键的目标是使c#面向组件。我们向语言自身加入了你在编写组件时所需要的所有概念。例如属性[译注:即property,翻译为“属性”,由来已久。我怀疑如果先有attribute的话,property会不会被翻译为“性质”、“特性”,而attribute才是“属性”:jl]、方法、事件、特性[译注:即attribute,截至目前,此名词译法仍较混乱。有的翻译和property不区分,也译为“属性”;有的译为“特性”;有的译为“属性信息”。在该名词译法尚未统一之前,本着精简原则,笔者先把它翻译成“特性”。但注意,xml中的attribute的译法一般比较统一,即为“属性”(因为xml中没有一个类似于property的东西会与之混淆)。因此,本文最后交叉描述c#和xml的部分,请留心“特性”、“属性”各有所指。]和文档等,它们都是一流的语言结构。我们对特性所做的工作是全新的和创新的。利用特性可为任何对象加入有类型的、可扩展的元数据。这在目前任何其它程序语言里都看不到的。c#也是第一个合并xml注释标记的语言,编译器可以用其直接从源码中生成可读的文档。

     另外一个重要的概念是我所说的“一站购物式软件”[one-stop-shopping software]。一旦你用c#写代码,你就在这一个地方写了一切。不再需要头文件、idl(接口定义语言)文件、guid和复杂的接口。因为它是自包容的单元。一旦用这种方式写自描述的代码,你就可以把你的软件嵌入到asp页面或植入各种不同的环境,这在以前是不可能的。

     但是让我们再回到组件这个重要的概念。语言是否应该支持属性或事件,业界有很多争论。没错,我们是可以用方法表达这种概念。我们可以用诸如“get”或“set”之类的程序块的命名模式模拟属性的行为。我们可以用接口和实现接口的适配器并转发到对象。这都是可能实现的,就象可能在c语言里进行面向对象编程一样。只是它太困难了,需要太多的手工劳动,为了表达你真正的思想,你最终不得不去做所有的工作。我们认为是时候了,应该有个语言使得创建组件变得容易些。今天程序员在创建软件组件。他们并不是创建整个应用或整个类库。每个人都是在创建从宿主环境提供的基组件继承下来的组件。这些组件重载一些方法和属性,它们处理一些事件,并把组件安装回系统。树立这些概念是关键的第一课。

osborn:

     你最近在介绍c#时,第一张幻灯片上面写着:“c/c++家族里第一个面向组件的语言”。

hejlsberg:

     是的。这是我的首要目标之一。我们谈论一切如何都是对象,这也非常关键。以前象smalltalk和lisp语言都可以这么做,但代价昂贵。我认为c#包含一些优美有趣的创新使得组件开发容易些。例如装箱和拆箱的概念。装箱可以使一个值类型的值转换为一个对象,拆箱可以使一个对象转换为一个简单类型的值。这在以前或许也有,但我们把它应用于语言的方式是一种优美的创新。

     我们努力避免用“象牙塔“的方式设计c#和.net框架。我们承受不起重写我们所有的软件的负担。业界也负担不起,特别是今天我们正转移到internet时代。你要善于利用你已经拥有的。所以,我认为互操作性也是关键的。我们致力于为程序员提供所有符合internet标准的可互操作的正确的解决方案,例如http、html、xml以及微软已经存在的技术。所以你不会有如坠深渊的那一刻—发现新的.net框架下没有提供你用的一些东西,或者你意识到你想利用一些已经存在的api或组件的时候。你已经看到我们已把所有com的互操作能力内建入语言和公共运行时;你已经看到可以使用dllimport特性导入已存在的dll[动态连接库];你已经看到即使那些都不能遂你愿,我们也有不安全代码的概念。不安全代码允许你写使用指针的内联c代码,可以做不安全的造型转换,可以抑制内存从而使其不会被意外地垃圾收集[译注:此处用作动词j]。

     关于不安去代码有很多争论,人们似乎认为我们在吸毒或是在干什么别的坏事。我认为这是个误会。代码不会仅仅因为标记了“不安全”就表示它不受管制。当然,我们不会扔出不安全的指针使人们容易受到从internet下载的不安全代码的攻击。不安全代码被深深地约束在安全的环境里。我们提供这样的弹性:1.呆在受管制的代码箱里完成工作而不会坠入深渊;2.转入一个不同的语言使用一个不同的编程模型写本地代码。如果你停留在这个箱子里,我们会使代码更加安全,因为系统知道它要干什么。事实上,即使你写不安全代码也不意味着你离开了受管制的空间。你的不安全代码会变得更有效率。

osborn:

     请给我多讲一些在受管制的环境里处理不安全代码的机制。

hejlsberg:

     好的。描述受管制的执行环境比如smalltalk、java和.net公共语言运行时一个重要特征是它们提供垃圾收集机制。为了提供垃圾收集机制,至少要提供一个现代的垃圾收集器,一个“标记和清扫”垃圾收集器。比起传统不受管制代码来说,你必须更多地了解正在执行的代码。为了找出要排除的死对象,你必须能遍历堆栈,找到所有活动的根,并指出哪些对象是活动的哪些是不再被访问的。然而,为了能够达到这个目标,你必须和你执行的代码紧密协作。代码必须具有更好的描述性。它要告诉你它是怎么分布在堆栈里的,它的局部变量存放在何处等等。

     当我们在c#中编写不安全代码时,你可以做不是类型安全的事,比如指针操作。标记为不安全的代码并非绝对执行在不可信任的环境里。为了使之执行,你必须授予信任,否则,代码将不会执行。从这一点来看,和其它本地代码并无区别—真正的区别是它们仍然运行在受管制的空间里。你写的方法有一个描述表,它告诉你哪些对象是活动的,因此,不管什么时候你进入这些代码,你都不必穿越列集边界。否则,当你进入非描述性的、不受管制的代码(比如通过java本地接口),你不得不在堆栈上设置一个水印或设置一个屏障。你必须重新列集所有箱子外的参数。一旦开始使用对象,你必须对你触及的东西小心翼翼,因为gc[垃圾收集器]仍然在另一个不同线程里运行。如果你不使用一些隐晦的方法锁定对象从而正确地抑制垃圾收集器,它可能会移去对象。如果你忘记那么做,那你将会不走运。

     我们采用了一个不同的方式。我们说过,“让我们集成这个到语言中去。让我们提供声明,例如fixed声明,它可以让你抑制对象以和gc协作并集成之。”用这种方法,我们提供最佳方式,带领所有已经存在的代码一起向前,而不是仅仅将它们抛弃。这是一个不一样的设计方式。

osborn:

     因此,你们对不安全代码的处理方式是—不安全代码的内存实际上是在垃圾收集器的监视之下?

hejlsberg:

     是的。但是,就象所谓的“购者自慎,不包退换”一样,它并不安全。你可以获取指针并做错事—当然,你在本地代码里也能干同样的错事。

osborn:

     我认为另一个易混淆的地方,是理解c#在哪儿停止和公共运行时从哪儿开始。与它从公共运行时库得到的相比,c#语言自身的创新是什么?

hejlsberg:

     好的,我想这个混淆来源于这样一个事实—当人们谈论java时,他们并不真的知道哪个是语言哪个是运行时。当人们谈论java时,混淆就发生了。哪个语言哪个是运行时?当他们谈论java时,他们到底指的是什么?java,语言?java,语法?还是java,平台?人们把这些不同的方面混成一团。我们的方式表明我们想成为一个跨语言的平台。我们将创建一个平台,它允许你进行多语言编程,并且共享一套公共的api(应用编程接口)。让我们承认这一点,一些人喜欢用cobol编程,一些人喜欢用basic编程,一些人喜欢用c++,还有一些人将会喜欢用c#—我希望。但是,我们不会试图告诉你,忘记你曾经做过的所有的事情吧,我们不会说,“现在只有一种语言,在这个竞争中不会再有创新了”。我们说业界因为弹性而友好。java是怎么来的?它的出现是因为在它前已经存在一些编程语言,而在它后也还将会出现一些编程语言。我们想打造一个平台,在此你可以偏爱某种语言但不会否定整个价值取向;我们想打造一个平台,它将是创新的。今天谁在帮助cobol程序员?谁把他们带入web?只有在.net平台上你才可以把富士通cobol嵌入asp页面。我的意思是,它真正是革命性的!

osborn:

     假定.net平台上支持多语言,那为什么选择c#而不是visual basic、c++甚至cobol?是什么使c#如此引人注目?

hejlsberg:

     首先,c#可以使我们从一张白纸开始。也就是说,我们没有任何向后兼容性的负担。这显然会使事情简化,无论从是从实现的立场还是从使用的立场都是这样。例如,在c#中,我们只有一类类型,并且总是被垃圾收集。而另一方面,受管制的c++有两套。因为它要保留非垃圾收集风格的编程。因此,在c#中,只需要你理解一些简单的概念。

语言是一个有趣的东西,它是一种口味;语言又是一件严肃的事情,它是程序员选择的一种生活方式。我是指,我们意识到我们不能走出来说,“这儿有个平台,你只可以使用一个基础语言。”即使在那个平台上用一种语言可以做所有的事情,人们还是可能不喜欢它的语法。他们可能喜欢大括号或者一些其它的程序块分界符。那是他们熟悉的。那是使他们感觉舒服并且富有生产力和能力的。我们对待c#的方式仅仅是为认为语言太复杂的c++程序员和认为丢失了一些c和c++语言特性的java程序员提供一个可选物。我们寻求一个简化c++的方式并投入到一个多语言的平台中,它提供更大的互操作性,并且它提供完备的组件概念等等。

goodhew:

一件有趣的事情来自于我们对开发者跟踪调查,60%以上的职业开发人员使用两种或更多的语言去创建他们的应用。特别是当我们问他们都用哪些开发工具的时候,我们得到的答案是:没有哪一种开发语言将会是终结者并且所有程序员都会使用它。正如anders早先所说,人们期望某种能够满足他们所做或他们所感的语法。这是一个个人选择。这也是整个.net平台所关心的—提供给开发者一个语言实现选择。我想我们做了件漂亮的工作。你基本上可以在visual basic.net和c#中做同样的事情。visual basic对大多数程序员来说仍然是易接受的。c#则具有更多的活动空间并且比vb更富威力。

osborn:

     这意味着在c#中可用更少的声明实现更多?

hejlsberg:

     是的。意味着通过不安全代码,你可以得到更多的能力。

osborn:

     也就是说,不能在vb中写不安全代码?

hejlsberg:

     是的。不可以。

goodhew:

     但是,基本上,两种语言都可以做同样的事。和visual studio 6相比,这是一个根本性的改变。在visual studio 6.0中,如果你想创建多线程的mts对象,并且你是一个vb程序员,你就没招。你不得不用c++。现在,有了.net框架,你可以使用任何一种你喜欢的语言。

hejlsberg:

     这就是我在一般会议谈话里说过的,.net框架提供一致的编程模型。在语言和框架的进化过程中,我们一贯似乎都是把一种程序语言绑死在特定的api和特定的编程方式上。vb是快速应用开发工具;mfc(微软基础类)是子类化的方式;asp则是把东西塞到web页面中。在每一种情况下,你对编程模型的选择决定了你对程序语言和可使用的api的选择。每次当你变换框架时,它都增加了你学习新语言和api负担的工作量。我们真正努力统一这一切,我们提供一套api,一套支持可视化设计的工具,我们还提供一个可以任选一种适合你的语言的弹性。

osborn:

     我不知道这对那些使用象vbscript和jscript脚本语言的有什么作用?

hejlsberg:

     .net框架下奇妙的事情之一是使脚本语言能够编译。看看asp+,现在,实际上,在你的页面里运行的是真正编译的代码。它不是后绑定的、调度查找的—如果用户没有点击页面,你不会看到运行时错误。asp+开发者可以使用visual basic.net完整的威力而不是vbscript。并且第一次,他们可以使用perl、python和其它流行语言,如果他们这么选择的话。

petrusha:

     服务端的javascript现在也能被编译?

hejlsberg:

     没错。

goodhew:

.net框架使得使用脚本语言就象用具有完全特性的语言一样,因为它们现在访问的是一个真正的编程框架并且访问的是同一基类api。你应该看看搞jscript实现的伙计们都已经实现了什么。[编注:jscript是微软对ecma 262语言规范(ecmascript 版本 3)的实现,只有一些很小的例外(为了保持向后的兼容性),jscript是对ecma标准的完整实现]。所以.net平台提供了一个公共语言框架,对脚本写作者来说,具有极大的好处。

osborn:

     我们已经讨论关于java、c++和脚本。在pdc[译注:(微软)职业开发者会议],我听很多人争论.net il(il是微软中间语言,所有编译器都必须产生它以运行在.net框架上)和运行于java虚拟机中的java字节码并没有什么不同。从你的谈话中,显然你并不同意这一点。你介意进一步评论它们之间的区别吗?

hejlsberg:

     好的。首先,il的思想是一个很老的思想了。你可以追溯这个概念到ucsd pascal p-machine(一个早期的个人计算机pascal实现)或者smalltalk。p-code曾被basic和visual basic使用,word的一个组成部件,内部使用p-code引擎,因为它更精简。所以,p-code并不是什么新东西。

     我认为,我认为我们使用的il的方式对此感兴趣:我们给你一个选择—如果你愿意—你可以控制把il编译或翻译为本地代码的时机。实际上,使用受管制的 c++,你可以直接从源程序生成本地代码。受管制的 c++还可以生成il,就象c#和vb那样。当你安装你的代码时,我们给你一个编译选项,可把il编译成本地代码。因此,当你运行它们时,就不会有即时编译负担。我们还给你提供了一个动态运行和编译代码的选项—即时编译。有了il,就给你带来了很多便利,比如它提供了这些能力:移植到不同的cpu结构并引入真正的类型安全并在此之上创建安全的系统。

我认为我们il的设计和java字节码关键的不同在于,我们做出了超前的决定—不用解释器。我们的代码永远本地运行。因此,即使产生il,你也不会运行解释器。我们还有不同风格的jit。对于精简框架,我们有econnojit,就象我们称呼它的一样,它是一个非常简单的jit[编注:精简.net是.net框架的一个子集,是为移植到其它设备和平台设计的]。对于桌面版,我们有完全功能的jit。我们甚至有可和我们的c++编译器共用一个后端的jit。不过,这都会比较耗时,因此你只应该在安装时使用它们。

     一旦你做出偏向于执行本地代码而不是解释码的决定,它就会深深地影响il设计。它改变了应该包括那些指令,应该包括哪些类型信息,以及它应该如何传输。如果你仔细看看两个il[译注:即.net il和java字节码],你就会发现它们非常不同。从某种意义上讲,我们的il是类型中立的。指令里没有指定参数类型的信息。进一步说,它是靠已经压栈的东西推断出来的。这种方式使il更为精简。无论如何,一个jit编译器需要知道哪些信息,因此没有理由在指令里携带它们。所以,最终我们做出了不同的设计决定,而这使得容易把il翻译成本地代码。

osborn:

     解释方式和你描述的方式有何不同?

hejlsberg:

     解释器的核心是一个循环—从p-code流取得一些字节,然后进入一个大大的switch[译注:类似于程序语言里的switch...case]声明:“哦,这是add指令,因此它到这儿来,但是这不是…”等等。

     解释器模拟cpu。我们反其道而行之,我们只走一条道,我们一直都走一条道,我们把指令翻译为机器码。现在,在econojit的情况下,机器码实际上非常简单,它只创建一个调用和压栈指令的列表,并且调用运行时帮助器,然后运行时帮助器替换这个列表。当然,这个代码比解释器代码执行得快。

osborn:

     让我用一句话来总结一下:你们完全编译代码。因此当你编译完时,字节已经完全准备好运行了,尽管从il翻译成机器码的时机可能不同。

hejlsberg:

     是的。但是,如果它是在一个内存受限的小设备的环境里,有可能当运行完就被扔掉了。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表