A Comparative Overview of C#中文版(上篇) 作者:Ben Albahari 公司:Genamics 日期:2000年7月31日初版,2000年8月10日修订。 感谢以下人士支持和反馈(按字母先后顺序):Don Box、 C.R. Manning、 Joe Nalewabau、 John Osborn、 Thomas Rhode & Daryl Richter。 译者:荣耀 【译序:C#入门经典!希望文中针对新手的译注不会影响阅读的流畅性。限于译者时间和能力,文中倘有讹误,当以英文原版为准】 本文将以C#提供的新的编程方式以及它是如何改进两个近邻—Java和C++为中心。C#在很多方面和Java用了类似的方式改进C++。因此,我不打算重复诸如单根对象层次的优点之类的东西。正文将以C#和Java的相似之处概述开始,然后着重探究C#新特性。 背景 2000年6月,微软同时宣布了.NET平台和一个名为C#的新的编程语言。C#是一个很好地融合了简单、表达力、性能的强类型的面向对象的语言。.NET平台以公共语言运行时(类似于Java虚拟机)和一个可被多种语言(它们可以通过编译成中间语言从而可以协同工作)共用的库为中心。C#和.NET有那么一点共生关系—C#的一些特性和.NET协作得很好,反之亦然(尽管.NET的目标是和多种语言很好地协作)。本文主要关注于C#,但视需要偶尔也会提及.NET。C#的设计借鉴了多种语言,但最主要的还是Java和C++。它是由Anders Hejlsberg(大名鼎鼎的Delphi【译注:说成Object Pascal更合适些】语言设计师)和Scott Wiltamuth共同设计的。 目录 1. C#和Java 2. 属性 3. 索引器 4. 委托 5. 事件 6. 枚举 7. 集合和foreach语句 8. 结构 9. 类型一致 10.操作符重载 11.多态 12.接口 13.版本处理 14.参数修饰符 15.特性【译注:即attribute,我在《C#首席设计师Anders Hejlsberg专访》译文中(参见CSDN的http://www.csdn.net/develop/article/11/11580.shtm)曾说过,到目前为止,该词译法仍较混乱,甚至和property不分,都被译为“属性”(Visual Studio.NET 7.0 Beta 2 的联机文档就是如此)。但本文中,仍将其译为“特性”,以示区分】 16.选择语句 17.预定义类型 18.字段修饰符 19.跳转语句 20.组合体、名字空间和访问级别【译注:Assembly一词译法比较混乱,有的译为“配件”,有的译为“组件”,有的译为“组合体”,而Visual Studio.NET 7.0 Beta2联机文档上则译为“程序集”,从技术上讲,这个译法说的倒很事实,但总感觉和这个词的外观远了点,在译法尚未统一之前,本文暂译为“组合体”】 21.指针运算 22.多维数组【译注:这一节里还谈到了交错数组】 23.构造器和析构器 24.受控执行环境 25.库 26.互用性 27.结论 1.C#和Java 下面是C#和Java共有的特性列表,目的都是为了改进C++。这些特性虽非本文重点,但了解它们之间的相似之处还是很重要的。 l编译为机器独立、语言独立的代码,运行在受控执行环境里; l采用垃圾收集机制,同时摒弃了指针(C#中,指针被限制在标为unsafe的代码内使用); l强有力的反射能力; l没有头文件,所有的代码都在包或组合体里,不存在类声明的循环依赖问题; l所有的类都派生自object,且必须用new关键字分配在堆上;【译注:Java中为Object;C#中为object,相当于.NET的System.Object】 l当进入标为锁定/同步代码时,通过在对象上加锁来支持多线程;【译注:例如Java中可对方法施以synchronized关键字,在C#中可使用Monitor类、Mutex类、lock语句等等】 l接口支持—多继承接口,单继承实现; l内部类; l类继承时无需指定访问级别;【译注:在C++中,你可以这么做:class cls2: private cls1{};等等】 l没有全局函数或常量,一切都必须属于类; l数组和字符串都保存长度记数并具边界检查能力; l永远使用“.”操作符,不再有“->”、“::”操作符; lnull和boolean/bool是关键字;【译注:Java中为boolean、C#中为bool,相当于System.Boolean】 l所有的值在使用前必须被初始化; lif语句不能使用整型数为判别条件; ltry语句块后可以跟finally从句。【译注:标准C++不可以,但Visual C++对SEH做了扩展,可以用__try和__finally】 2.属性 对于Delphi和Visual Basic的用户来说,属性是个熟悉的概念。使用属性的目的是将获取器/设置器[译注:原文为getter/setter]的概念正式化,这是一个被广泛使用的模式,尤其是在RAD(快速应用开发)工具里。 以下是你可能在Java或C++里写的典型代码: foo.setSize (getSize () + 1); label.getFont().setBold (true); 同样代码在C#里可能会变成: foo.size++; label.font.bold = true; C#代码对于使用foo和label的用户来说更直观、更可读。在实现属性方面,差不多同样简单: Java/C++: public int getSize() { return size; } public void setSize (int value) { size = value; } C#: public int Size { get {return size;} set {size = value;} } 特别是对于可读写的属性,C#提供了一个处理此概念的更清爽的方式。在C#中,get和set方法是内在的,而在Java和C++里则需人为维护。C#的处理方式有诸多优点。它鼓励程序员按照属性的方式去思考—把这个属性标为可读写的和只读的哪个更自然?或者根本不应该为属性?如果你想改变你的属性的名称,你只要检查一处就可以了(我曾看到过中间隔了几百行代码的获取器和设置器【译注:此处是指C++(Java)里对同一个数据成员/字段(一般来说是)的获取器和设置器】)。注释也只要一处就可以了,这也避免了彼此同步的问题。IDE【译注:集成开发环境】是可以帮助做这个事的(事实上,我建议他们这么做【译注:此处的“他们”应该是指微软有关人员】),但应该牢记编程上的一个基本原理—尽力做好模拟我们问题空间的抽象。一个支持属性的语言将有助于获得更好的抽象。 【作者注:关于属性的这个优点的一个反对意见认为:当采用这种语法时,你搞不清是在操纵一个字段还是属性。然而,在Java(当然也包括C#)中,几乎所有真正复杂一点的类都不会有public的字段。字段一般都只具有尽可能小的访问级别(private/protected,或语言所定义的缺省的),并且只通过获取器和设置器方法暴露,这也意味着你可以获得优美的语法。让IDE解析代码也是完全可行的,可用不同的颜色高亮显示属性,或提供代码完成信息以表明它是否是一个属性。我们还应该看到,如果一个类设计良好,这个类的用户将只关心该类的接口(或规范)【译注:此处是指该类向其客户公开(不单单是public,对其派生类来说,也可能是protected)的方法、属性(C++/Java无显式属性概念)等,这里的客户包括其派生类等等】,而不是其内部实现。另外一个可能的争论是属性不够有效率。事实上,好的编译器可以内联仅返回某个字段的获取器,这和直接访问字段一样快。说到底,即使使用字段要比获取器/设置器来的有效,使用属性还有如下好处—日后可以改变属性的字段【译注:是指可以改变获取器/设置器的实现代码部分,比如改变获取器/设置器里所操作的字段,也可以在获取器/设置器里做一些校验或修饰工作等】,而不会影响依赖于该属性的代码】 3.索引器 C#通过提供索引器,可以象处理数组一样处理对象。特别是属性,每一个元素都以一个get或set方法暴露。 public class Skyscraper { Story[] stories; public Story this [int index] { get { return stories [index]; } set { if (value != null) { stories [index] = value; } } } //... } Skyscraper empireState = new Skyscraper (/*...*/); empireState [102] = new Story ("The Top One", /*...*/); 【译注:索引器最大的好处是使代码看上去更自然,更符合实际的思考模式】 4.委托 委托可以被认为是类型安全的、面向对象的函数指针,它可以拥有多个方法。委托处理的问题在C++中可以用函数指针处理,而在Java中则可以用接口处理。它通过提供类型安全和支持多方法改进了函数指针方式;它通过可以进行方法调用而不需要内部类适配器或额外的代码去处理多方法调用问题而改进了接口方式。委托最重要用途是事件处理,下一节将通过一个例子加以介绍。 5.事件 C#提供了对事件的直接支持。尽管事件处理一直是编程的基本部分,但令人惊讶的是,大多数语言在正式化这个概念上所做的努力都微乎其微。如果看看现今主流框架是如何处理事件的,我们可以举出如下例子:Delphi的函数指针(称为闭包)和Java的内部类适配器,当然还有Windows API消息系统。C#使用delegate和event关键字提供了一个清爽的事件处理方案。我认为描述这个机制的最好的办法是举个例子来说明声明、触发和处理事件的过程: // 委托声明定义了可被调用的方法签名【译注:这里的签名可以理解为“原型”】 public delegate void ScoreChangeEventHandler (int newScore, ref bool cancel); // 产生事件的类 public class Game { //注意使用关键字 public event ScoreChangeEventHandler ScoreChange; int score; // 属性Score public int Score { get { return score; } set { if (score != value) { bool cancel = false; ScoreChange (value, ref cancel); if (! cancel) score = value; } } } } // 处理事件的类 public class Referee { public Referee (Game game) { // 监视game中的score的分数改变 game.ScoreChange += new ScoreChangeEventHandler (game_ScoreChange); } // 注意这个方法签名和ScoreChangeEventHandler的方法签名要匹配 private void game_ScoreChange (int newScore, ref bool cancel) { if (newScore < 100) System.Console.WriteLine ("Good Score"); else { cancel = true; System.Console.WriteLine ("No Score can be that high!"); } } } //测试类 public class GameTest { public static void Main () { Game game = new Game (); Referee referee = new Referee (game); game.Score = 70;//【译注:输出 Good Score】 game.Score = 110;// 【译注:输出 No Score can be that high!】 &nb
|