站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Java 专题Anders Hejlsberg论为什么不在c#引入类似java的checked exceptions

Anders Hejlsberg论为什么不在c#引入类似java的checked exceptions

摘要: Anders Hejlsberg论为什么不在c#引入类似java的checked exceptions

Anders Hejlsberg论为什么不在c#引入类似java的checked exceptions
这是Anders Hejlsberg发表在http://www.artima.com系列短文中的一篇。这个系列
论述了他设计c#的思路,集中讨论了c#中借鉴java地方,读来颇有受益,我便擅
自把它翻译成中文。
本篇中参与谈论的还有两人,一个是Bruce Eckel,Thinking系列的作者(以下简称
BE);一个是Bill Venners,“Inside JVM”作者,www.Artima.com的主编(简称BV)。
BE:C#没有checked exceptions。你是如何决定不把它引入C#中的?
AH:我觉得checked exceptions带来两个问题:可扩展性和版本控制(scalability
and versionability)。我知道你也写过关于checked exceptions的文章,你好像
也同意我们的观点。[i]
BE:我曾经觉得checked exceptions是个很了不起的创意。
AH:没错。坦率的说,乍看起来,他们确实很棒,而且这个创意本身并没有错。我
完全同意checked exceptions是个很好的特性。只是某些特定的实现(比如说,
java的实现方式)方式会带来问题。在java中,你只是把一类问题转换成另一类,
我不觉得它(java的方式)使我的生活变得简单,你只是把它变成另一类问题。
BE:C#设计小组中的其他成员对这个问题有不同的看法吗?
AH:没有。我想,我们对这个问题达成很广泛的一致。C#现在对这个问题基本上不
作表态。一旦我们发现了更好的解决方式,(相信我,我们仍在思考这个问题),
我们会把它公布出来。我一直都持这样的观点:如果你对一个问题没有准确的说法
,没有一个完美的解决法案,那你干脆什么也不说,而不是试图去建立一个解决它的大框架。
如果你让初级程序员写一个日历控件,他们常常会这样对自己说,“天啊,我决定
写一个全世界最棒的日历控件,它将针对不同的历法展现出多态行为。它会有各种
花哨的特性。”他们只有两个月的时间来完成这个任务,却把几乎所有的时间用在
搭建程序的框架上,而只花两天的时间来写真正有关日历控件的代码。然后,他们
不得不自我安慰说,我会在下一版中做得更好些。
这样的设计方法从一开始就错了。而我却不断看到这种现象,一而再,再而三的发
生。因此我坚信简单就是美。除非你能解决一个通用问题,否则不要把一个解决特
定问题的方法变成一个通用的框架(framework),因为你并不知道该如何设计这个框架。
BE:极限编程说,“用最简单的方法来解决问题”。
AH:是啊,爱因斯坦不也说过,“用最简单的方法来解决问题,但不要过度简单”
("Everything should be as simple as possible, but no simpler"),我对
checked exceptions的担心是,它增加了程序员的负担。[ii]
当你看到程序员使用了带有throws子句的函数时,他们的代码变得多么扭曲,你就
会意识到checked exceptions 其实并没有帮助他们。这实际上是武断的API设计者
在告诉使用者,你该怎么怎么办,而不是使用者决定他该怎么怎么办,这根本就是本末倒置!
BV:你提到的可扩展性和版本控制(scalability and versionability)问题,你
能进一步澄清一下吗?
AH:好的,我们先来看版本的问题,它比较容易理解。假设,我写了一个foo函数,
他丢出三个异常A,B,C。在下一个版本中,我决定对foo增加新的功能,而它也将因
此丢出一个新的异常D。这是一个带来麻烦的改变,因为已有的代码如果不作修改,
根本无法处理这个新的异常D。也就是说新版本中新异常的出现会给使用它的程序员
带来麻烦。这就好像往公有的接口添加一个新的方法一样。而实际上,一个公有的
接口,一旦发布会后,就不应该改变的。如果你想添加新的方法,你应该发布一个
新的接口。异常情况也类似:你要么写一个全新的方法foo2 丢出A,B,C,D,4个异常
,要么在你的实现中捕获新的异常D,再将它转换成A,B,C其中的一种。
BV:但是,对于没有checked exceptions的编程语言不也同样存在这个问题吗?如
果新版本的foo函数丢出了新异常,使用者不一样也得考虑如何处理他们?这不一样
也给他们老代码带来麻烦吗?[iii]
AH:不是这样的。因为在很多情况下,人们不关心(这些异常)。他们也不打算处
理这些异常。在他们的消息循环底部有段异常处理代码,这段代码仅仅弹出一个对
话框告诉你,出了异常,然后一切继续运转。(实际情况就是这样)。程序员为了
保护他们的代码会在所有地方写上try finally,这样当异常出现是他们能正常退出
,但他们不关心如何处理这些异常。
在java中的实现,throws语句并不强迫你处理异常。如果你不处理他们,java强迫
你必须确认有哪些异常会丢出。你或者捕获这些异常,或者把他们放到你的throws
子句中。为了避免这样的麻烦,人们会做一些愚蠢的事,比如说他们会在每个函数
后加上“throws Exception”,这完全违背了checked exceptions的初衷。当你看
到人们些一大堆这样冗长的代码时,你就会意思到checked exceptions根本没有起到任何作用。
BV:所以你认为更多的情况是,人们知道最终会有通用catch块来处理异常,所以他
们自己并不处理它们。(So you think the more common case is that callers
don't explicitly handle exceptions in deference to a general catch clause
further up the call stack?)
AH:有意思的是人们为什么会认为异常中重要的是处理它们?这其实并不重要的。
我个人认为,一个编写良好的程序,try finally (或C#中的using,类似于try
finally)对try catch比例应该是十比一。
BV:什么是finally?
AH:在finally中,你可以保护你的代码不受异常的影响,但你并不处理异常。异常
处理的代码你可以放在其他的地方。所有事件驱动的程序,像任何一种现代的图像
用户程序(modern UI),你会在你消息循环中放一段异常处理代码,只有在程序走
到这块时才处理他们。但你必须确认释放了所有资源,做了所有必要的清理工作(
在finally块中),这样你才能保护自己始终处在一个一致的,正确的状态。[iv]你
不会想要一个程序有100不同的地方来处理异常,并弹出对话框,万一你想改变弹出
对话框的方式呢?(那你将修改100处),这太可怕了。所以说处理异常的代码应该
集中起来(The exception handling should be centralized)。你只需要在异常
被处理前保护好你自己就行了。
BV:那checked exceptions带来的可扩展性问题(scalability)又是什么?
AH:scalability 和 versionability 有些关联。在小的程序中,checked
exceptions是非常吸引人的。在一个小的程序中,你可以看到你真的捕获
FileNotFoundException 异常,这多让人振奋啊!
当你只调用了一个API,确实是这样的。但当你开始构建一个由4,5子系统组成的大
系统时,麻烦就出现了。每个子系统会丢出四到十个异常,这些丢出异常就像一把
把梯子,让你爬得越来越高,你是不是开始觉得头晕目眩了?最终你可能碰到一段
代码丢出40异常的情况。而当你把这个大系统和另外一个系统结合在一起时,你甚
至会丢出80异常,这简直太可怕。
总的来说,当checked exceptions变得无法控制,人们就会采取一些极端的措施来
逃避它。所以你会看到他们干脆就写"throws Exception"。我甚至无法告诉你,有
多少次我看到人们这样写 :
try {
…}
catch() {}
catch() {} …
他们会自我安慰说,等我有时间了再把catch的代码补上,而实际上他们根本就不会
。这种情况下,checked exceptions 实际上降低了代码的质量。
所以,当把所有这些情况都考虑了之后,我决定我需要作更多的思考来决定是否把
某种形式的checked exceptions加入C#中。当然,能事先知道什么样的异常会丢出
,而且有某种工具来检验无疑是很有价值的。我不认为我们能定下什么严格而有效
的规则让编译器来做这个工作,当然我们可以做很多分析工作来检测可疑的代码,
推断出有哪些未捕获的异常,并把这些潜在的危险告诉用户。 --------------------------------------------------------------------------------
[i] http://www.mindview.net/Etc/Discussions/CheckedExceptions
[ii] 这个问题,笔者在James Gosling访华时也曾当面问他,可能是由于当时会场
的气氛,Gosling只是匆匆的回答说,他并不在意是不是给程序员增加了负担,他在
意的是程序的正确性。有意思的是,James Gosling 也在www.artima.com发表他对
checked exceptions见解:http://www.artima.com/intv/solid.html
[iii] 在“The Design and Evolution of C++”, Bjarne Stroustrup也提到,同
样也基于对这个问题的考虑,c++没有“Static Checking”(checked exceptions
)而是采用“Run time Checking”。而且Stroustrup建议,对于丢出新异常D,可
以让它从已有的异常中继承,这样既不影响已有代码,新的代码也可以处理它。(
这是Stroustrup在1990就作出的结论!)
[iv] 这其实Stroustrup一再强调的RAII。因为没有析构函数,不得不用finally来
模拟。(C++有多少特性从来就没有人很好理解!又有多少人把C++当成C with Class, Sigh!)
作者相关文章:
Understanding Swing’s Model(原作)
对该文的评论 人气:816
DavidGuo(2003-12-13 11:30:54)
scalability 译为 可伸缩性比较恰当。
可扩展性似为 Extensibility
TIYILON(2003-12-12 22:42:43)
关键是不可滥用异常。
ThinkX(2003-12-12 20:24:21)
↑返回目录
前一篇: Ant+junit的测试自动化
后一篇: Adding Undo and Redo to a Text Component