Anders Hejlsberg访谈:版本,虚函数和覆写

本访谈系列的翻译已经征得原作者的同意,转载请保留原作者和译者的链接。

Copyright © 1996-2005 Artima Software, Inc. All rights reserved

Versioning, Virtual, and Override
A Conversation with Anders Hejlsberg, Part IV
by Bill Venners with Bruce Eckel
September 15, 2003

版本,虚函数和覆写

翻译:刘晓伟

摘要
Anders HejlsbergC#的主架构师,与Bruce EckelBill Venners谈论了为什么C#的方法默认是非虚函数,以及为什么程序员必须显式 指定覆写(override)。

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

2003730号,Bruce Eckel(《Thinking in C++》以及《Thinking in Java》的作者)和Bill VennersArtima.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)。

非虚方法(Non-Virtual)是默认的

Bill Venners: Java里,类的方法默认是虚的——只要不被显式声明成final,它们就可以在子类里被覆写(overidden)。与此不同,在C#里,instance方法默认都是非虚的。要想让一个方法成为虚方法,程序员必须显示把它声明为virtualC#为什么要把非虚方法作为默认的呢?

Anders Hejlsberg: 有几个理由。一个是性能。我们观察到人们用Java写代码的时候,忘了给他们的方法加上final关键字。于是,这些方法就成了虚方法。因为它们是虚方法,它们的性能就不那么好。作为一个虚方法总会带来一些性能上的负担。这是问题之一。

更重要的一个问题是版本问题。关于虚方法有两派不同的意见。学院派认为,“所有的东西都应该是虚的,因为说不定什么时候我就想要覆写它。”实用主义学派(他们的观点来自于构建运行于现实世界的真实程序)认为,“对于要声明成virtual的东西我们应该格外小心。”

当我们在某个平台上把某些东西弄成virtual的时候,我们是在就它将来如何演化做出非常多的承诺。对于一个非虚方法,我们承诺当你调用这个方法的时候,xy可以发生就可以了。当我们在一个API里发布一个虚方法的时候,我们不仅仅要保证当你调用这个方法的时候,xy可以发生。我们还要保证当你覆写(override)这个方法的时候,我们会根据其它被覆写的函数按照特定的顺序来调用它,并且它的状态会放在某个不变量里。

每次当你在某个API里提到virtual的时候,你就是在创建一个回调的挂钩(call back hook)。作为一个操作系统或者API框架的设计者,你必须对这个问题格外谨慎。你不希望用户在一个API里的任意地方覆写并且实现挂钩程序,因为你没法做出那些承诺。而且人们在把某些东西弄成virtual的时候,可能没有完全明白他们需要为此做出的承诺。

进来的和出去的约定(Incoming and Outgoing Contracts

Bill Venners: 听起来你并不十分关心覆写方法的人没有正确地实现你对于调用者所做出的承诺。你所担心的是你必须要对覆写这个方法的人所做出的种种承诺。

Anders Hejlsberg: 实际上两个我都关心。就这个问题来说,Virtual有两个方面:进来的(Incoming)和出去的(Outgoing)。对于思考进来的这方面所应遵循的约定(contract)大家都毫无问题。而对于出去的方面所应遵循的约定就非常差劲了。

Bill Venners: 进来的和出去的约定,你是指什么?

Anders Hejlsberg: 进来的约定是当我调用一个方法的时候需要用到的。它让我思考在调用一个方法之前需要做什么以及当方法返回以后都发生了些什么。出去的约定是当我覆写(override)一个方法的时候需要用到的。如果你看看大多数API,实际上它们对在你要覆写一个虚方法的时候需要做些什么所给出的文档是很差劲的:在你调用之前不变的是什么?调用之后哪些该为真(true)?哪些方法是不能通过你的实现来调用的?诸如此类。我想把所有方法都包括进来的和出去的方面作为默认情况是非常危险的。轻信每个人都会写清楚文档告诉你什么时候该覆写以及覆写的时候哪些是不变的,是非常危险的。

我可以示范给你一个真正现实世界中的版本问题,这是我们从Java的经验中真真切切看到的一个问题。无论什么时候他们发布一个Java类库的新版本,前后兼容性就被打破了。当他们在基类里引入一个新方法,如果有人在派生类里有一个与之同名的方法,这个方法就被覆写了——除非它有一个不同的返回类型,否则编译就不会再通过了。问题是Java(以及C++)没有捕获程序员使用virtual的时候的意图。

Virtual有两层意思

Anders Hejlsberg: 当你说“virtual”的时候,你可能指的是两层意思中的一个。如果你没有继承一个具有同样签名的方法,那么这就是一个新的虚方法。这是第一层意思。否则它就覆写了被继承的方法。这是第二层意思。

从版本的角度来看,当声明一个方法为vritual的时候,让程序员说明他们的意图是很重要的。比如,在C#里,你必须显式说明自己想要使用的是virtual的哪一层含义。要声明一个新的虚方法,只要给它加上virtual就可以了。但是要覆写一个现有的虚方法,你必须给它加上override关键字。

事实上,C#并没有上述的版本问题,即便是在基类里引入一个已经在派生类里定义过的方法。在派生类里,假设你已经定义了foo为虚方法。现在我们(在基类里)引入了一个新的虚方法foo。好的,这没什么问题。现在有了两个虚的foo方法,但是虚函数表(VTBL)里也有两个入口。派生类的foo遮挡了基类的foo方法,但是这并没有什么问题。写派生类的foo方法的时候基类的foo方法还不存在呢,所以遮挡掉这个新的虚方法并没有什么问题。一切都按照原本预定的方式照常进行。

Bruce Eckel: 也就是说你在实践中看到这个版本问题多次发生,于是你就决定处理掉它?我记得你在Delphi里也做了类似的事情。

Anders Hejlsberg: 是的。

Bruce Eckel: 你对于编程语言的观点与我所交谈过的其他人有很大的不同。你的观点非常倾向于实用主义。

Anders Hejlsberg: 我经常把自己说成是一个实用主义者。很有意思,因为版本(versioning)最终成为支撑我们语言设计的柱石。如何在C#里覆写虚方法体现了这一点。另外,在C#里重载解析(overload resolution)的方式也区别于其它任何我所知道的语言,这也是出于versioning的原因。每当我们想要设计一个特性的时候,我们都会针对versioning反复斟酌一下。我们会问问,“versioning对此有什么影响?从versioning的角度来看它会如何运行?”事实上,以前大多数的语言设计很少从这个角度考虑问题。

Bruce Eckel: 你关心版本问题,主要是因为DLL hell问题吗?

Anders Hejlsberg: 是的,但是还源于我这些年的观察。十到十五年前,那时候我们的应用程序最多只有640K的内存可用,每年你都可以把所有的代码扔掉然后重写它们。重写大概需要一年的时间,这正好和下一个发布周期相吻合。所以你并不需要担心能否重用以前的东西。版本?那是什么东西?我们可是每次都从头开始。

很抱歉,我的回答打断了你的问题,但是我说的那个时代已经过去了。我们再也跟不上了,摩尔法则所带来的容量增长我们永远也用不完。现今,我们是通过越来越多地利用现有的基础设施和应用程序,得到更多的功能。随着系统的生命周期越来越长,版本问题就变得越发重要。

下周

Anders Hejlsberg访谈的下一部分将会在(2003年)929号,星期一贴出来。如果你想收到Artima.com上新文章每周简报的电子邮件,请订阅Artima Newsletter

反馈

对本文所描述的设计原则有自己的观点么?那么请到News&Ideas论坛讨论这篇文章, Versioning, Virtual, and Override.

资源

深入C#:微软主架构师Anders Hejlsberg访谈:

http://windows.oreilly.com/news/hejlsberg_0800.html

A Comparative Overview of C#:
http://genamics.com/developer/csharp_comparative.htm

Microsoft Visual C#:
http://msdn.microsoft.com/vcsharp/

Anders Hejlsberg不是Artima的采访对象中第一个提到品味的。Jim Waldo在他的访谈中针对构建一个由有品味的程序员组成的团队给出了几乎同样的评述:

http://www.artima.com/intv/waldo10.html

Ken Arnold’s的访谈有一整部分都是关于设计品味的——品味和美学(Taste and Aesthetics):
http://www.artima.com/intv/taste.html 

<script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值