在GameTest里,我們分別創(chuàng)建了一個(gè)game和一個(gè)監(jiān)視game的referee,然后,然后我們改變game的Score去看看referee對(duì)此有何反應(yīng)。在這個(gè)系統(tǒng)里,game沒(méi)有referee的任何知識(shí),任何類(lèi)都可以監(jiān)聽(tīng)并對(duì)game的score變化產(chǎn)生反應(yīng)。關(guān)鍵字event隱藏了除了+=和-=之外的所有委托方法。這兩個(gè)操作符允許你添加(或移去)處理該事件的多個(gè)事件處理器。 【譯注:我們以下例說(shuō)明后面這句話的意思: public class Game { public event ScoreChangeEventHandler ScoreChange; protected void OnScoreChange() { if (ScoreChange != null) ScoreChange(30, ref true);//在類(lèi)內(nèi),可以這么使用 } ,但在這個(gè)類(lèi)外,ScoreChange就只能出現(xiàn)在運(yùn)算符+=和-=的左邊】 你可能首先會(huì)在圖形用戶界面框架里遇到這個(gè)系統(tǒng)。game好比是用戶界面的某個(gè)控件,它根據(jù)用戶輸入觸發(fā)事件,而referee則類(lèi)似于一個(gè)窗體,它負(fù)責(zé)處理該事件。 【作者注:委托第一次被微軟Visual J++引入也是Anders Hejlsberg設(shè)計(jì)的,同時(shí)它也是造成Sun和微軟在技術(shù)和法律方面爭(zhēng)端的起因之一。James Gosling,Java的設(shè)計(jì)者,對(duì)Anders Hejlsberg曾有過(guò)一個(gè)故作謙虛聽(tīng)起來(lái)也頗為幽默的評(píng)論,說(shuō)他因?yàn)楹虳elphi藕斷絲連的感情應(yīng)該叫他“方法指針先生”。在研究Sun對(duì)委托的爭(zhēng)執(zhí)后,我覺(jué)得稱呼Gosling為“一切都是一個(gè)類(lèi)先生”好像公平些J 過(guò)去的這幾年里,在編程界,“做努力模擬現(xiàn)實(shí)的抽象”已經(jīng)被很多人代之以“現(xiàn)實(shí)是面向?qū)ο蟮模裕覀儜?yīng)該用面向?qū)ο蟮某橄髞?lái)模擬它”。 Sun和微軟關(guān)于委托的爭(zhēng)論可以在這兒看到: http://www.Javasoft.com/docs/white/delegates.html http://msdn.microsoft.com/visualj/technical/articles/delegates/truth.asp 】 6.枚舉 枚舉使你能夠指定一組對(duì)象,例如: 聲明: public enum Direction {North, East, West, South}; 使用: Direction wall = Direction.North; 這真是個(gè)優(yōu)雅的概念,這也是C#為什么會(huì)決定保留它們的原因,但是,為什么Java卻選擇了拋棄?在Java中,你不得不這么做: 聲明: public class Direction { public final static int NORTH = 1; public final static int EAST = 2; public final static int WEST = 3; public final static int SOUTH = 4; } 使用: int wall = Direction.NORTH; 看起來(lái)好像Java版的更富有表達(dá)力,但事實(shí)并非如此。它不是類(lèi)型安全的,你可能一不小心會(huì)把任何int型的值賦給wall而編譯器不會(huì)發(fā)出任何抱怨【譯注:你顯然不可以這么寫(xiě):Direction wall = Direction.NORTH;】。坦白地說(shuō),在我的Java編程經(jīng)歷里,我從未因?yàn)樵撎幏穷?lèi)型安全而花費(fèi)太多的時(shí)間寫(xiě)一些額外的東西來(lái)捕捉錯(cuò)誤。但是,能擁有枚舉是一件快事。C#帶給你的一個(gè)驚喜是—當(dāng)你調(diào)試程序時(shí),如果你在使用枚舉變量的地方設(shè)置斷點(diǎn),調(diào)試器將自動(dòng)譯解direction并給你一個(gè)可讀的信息,而不是一個(gè)你自己不得不譯解的數(shù)值: 聲明: public enum Direction {North=1, East=2, West=4, South=8}; 使用: Direction direction = Direction.North | Direction.West; if ((direction & Direction.North) != 0) //.... 如果你在if語(yǔ)句上設(shè)置斷點(diǎn),你將得到一個(gè)你可讀的direction而不是數(shù)值5。 【譯注:這個(gè)例子改一下,會(huì)更有助于理解: 聲明: public enum Direction {North=1, East=2, West=4, South=8, Middle = 5/*注意此處代碼*/}; 使用: Direction direction = Direction.North | Direction.West; if ((direction & Direction.North) != 0) //.... 如果你在if語(yǔ)句上設(shè)置斷點(diǎn),你將得到一個(gè)你可讀的direction(即Middle)而不是數(shù)值5】 【作者注:枚舉被Java拋棄的原因極有可能是因?yàn)樗梢杂妙?lèi)代替。正如我上面提到的,單單用類(lèi)我們不能夠象用別的概念一樣更好地表達(dá)某個(gè)特性。Java的“如果它可以用類(lèi)處理,那就不引入一個(gè)新的結(jié)構(gòu)”的哲學(xué)的優(yōu)點(diǎn)何在?看起來(lái)最大的優(yōu)點(diǎn)是簡(jiǎn)單—較短的學(xué)習(xí)曲線,并且無(wú)需程序員去考慮做同一件事的多種方式。實(shí)際上,Java語(yǔ)言在很多方面都以簡(jiǎn)化為目標(biāo)來(lái)改進(jìn)C++,比如不用指針,不用頭文件,以及單根對(duì)象層次等。所有這些簡(jiǎn)化的共性是它們實(shí)際上使得編程—唔—簡(jiǎn)單了,可是,沒(méi)有我們剛才提到的枚舉、屬性和事件等等,反而使你的代碼更加復(fù)雜了】 7.集合和foreach語(yǔ)句 C#提供一個(gè)for循環(huán)的捷徑,而且它還促進(jìn)了集合類(lèi)更為一致: 在Java或C++中: 1. while (! collection.isEmpty()) { Object o = collection.get(); collection.next() //... 2. for (int i = 0; i < array.length; i++) //... 在 C#中: 1.foreach (object o in collection) //... 2.foreach (int i in array) //... C#的for循環(huán)將工作于集合對(duì)象上(數(shù)組實(shí)現(xiàn)一個(gè)集合)。集合對(duì)象有一個(gè)GetEnumerator()方法,該方法返回一個(gè)Enumerator對(duì)象。Enumerator對(duì)象有一個(gè)MoveNext()方法和一個(gè)Current屬性。 8.結(jié)構(gòu) 把C#的結(jié)構(gòu)視為使語(yǔ)言的類(lèi)型系統(tǒng)更為優(yōu)雅而不僅是一種“如果你需要的話可以利用之寫(xiě)出真正有效率的代碼”的概念更好些。 在C++中,結(jié)構(gòu)和類(lèi)(對(duì)象)都可分配在棧或堆上。在C#中,結(jié)構(gòu)永遠(yuǎn)創(chuàng)建在棧上,類(lèi)(對(duì)象)則永遠(yuǎn)創(chuàng)建在堆上。使用結(jié)構(gòu)實(shí)際上可以生成更有效率的代碼: public struct Vector { public float direction; public int magnitude; } Vector[] vectors = new Vector [1000]; 這將把1000個(gè)Vector分配在一塊空間上,這比我們把Vector聲明為類(lèi)并使用for循環(huán)去實(shí)例化1000個(gè)獨(dú)立的Vector來(lái)得有效率得多。【譯注:因懷疑原文有誤,此處故意漏譯一句,但不應(yīng)影響你對(duì)這節(jié)內(nèi)容的理解】: int[] ints = new ints[1000];//【譯注:此處代碼有誤,應(yīng)為int[] ints = new int[1000];】 C#完全允許你擴(kuò)展內(nèi)建在語(yǔ)言中的基本類(lèi)型集。實(shí)際上,C#所有的基本類(lèi)型都以結(jié)構(gòu)方式實(shí)現(xiàn)的。int型只不過(guò)是System.Int32結(jié)構(gòu)的別名,long型不過(guò)是System.Int64結(jié)構(gòu)的別名等等。這些基本類(lèi)型當(dāng)然可被編譯器特別處理,但是語(yǔ)言本身并無(wú)區(qū)別【譯注:意思是語(yǔ)言自身對(duì)處理所有類(lèi)型提供了一致的方法】。在下一節(jié)中,我們可看到C#是如何做到這一點(diǎn)的。 9.類(lèi)型一致 大多數(shù)語(yǔ)言都有基本類(lèi)型(int、long等等)。高級(jí)類(lèi)型最終是由基本類(lèi)型構(gòu)成的。能以同樣的方式處理基本類(lèi)型和高級(jí)類(lèi)型通常來(lái)說(shuō)是有用處的。例如,如果集合可以象包容sting那樣包容int是有用的。為此,Smalltalk通過(guò)犧牲些許效率象處理string或Form一樣來(lái)處理int和long。Java試圖避免這個(gè)效率損失,它象C和C++那樣處理基本類(lèi)型,但又為每一個(gè)基本類(lèi)型提供了相應(yīng)的包裝類(lèi)—int包裝為Integer,double包裝為Double。C++模板參數(shù)可接受任何類(lèi)型,只要該類(lèi)型提供了模板定義的操作的實(shí)現(xiàn)。 【譯注:在Java中,你可以這么寫(xiě): int i = 1; double d = 1.1; Integer iObj = new Integer(1); Double dObj = new Double(1.1); 以下寫(xiě)法是錯(cuò)誤的: int I = new int(1); Integer iObj = 1; 】 C#對(duì)該問(wèn)題提供了一個(gè)不同的解決方案。在上一節(jié)里,我介紹了C#中的結(jié)構(gòu),指出基本類(lèi)型不過(guò)是結(jié)構(gòu)的一個(gè)別名而已。既然結(jié)構(gòu)擁有所有對(duì)象類(lèi)型擁有的方法,那代碼就可以這么寫(xiě): int i = 5; System.Console.WriteLine (i.ToString()); 如果我們想象使用一個(gè)對(duì)象那樣使用一個(gè)結(jié)構(gòu),C#將為你裝箱該結(jié)構(gòu)為對(duì)象,當(dāng)你再次需要使用結(jié)構(gòu)時(shí),可以通過(guò)拆箱實(shí)現(xiàn): Stack stack = new Stack (); stack.Push (i); // 裝箱 int j = (int) stack.Pop(); //拆箱 拆箱不僅是類(lèi)型轉(zhuǎn)換的需要,它也是一個(gè)無(wú)縫處理結(jié)構(gòu)和類(lèi)之間關(guān)系的方式。你要清楚裝箱是做了創(chuàng)建包裝類(lèi)的工作,盡管CLR可以為被裝箱的對(duì)象提供附加的優(yōu)化。 【譯注:可以這么認(rèn)為,在C#中,對(duì)于任何值(結(jié)構(gòu))類(lèi)型,都存在如下的包裝類(lèi): class T_Box //T代表任何值類(lèi)型 { T Value; T_Box(T t){Value = t;} } 當(dāng)裝箱時(shí),比如: int n = 1; object box = n; 概念上相當(dāng)于: int n = 1; object box = new int_Box(i); 當(dāng)拆箱時(shí),比如: object box = 1; int n = (int)box; 概念上相當(dāng)于: object box = new int_Box(1); int n = ((int_Box)box).Value;】 【作者注:C#的設(shè)計(jì)者在設(shè)計(jì)過(guò)程中應(yīng)該考慮過(guò)模板。我懷疑未采用模板有兩個(gè)原因:第一個(gè)是混亂,模板可能很難和面向?qū)ο蟮奶匦匀诤显谝黄穑鼮槌绦騿T的帶來(lái)了太多的(混亂)設(shè)計(jì)可能性,而且它很難和反射一起工作;第二點(diǎn)是,如果.NET庫(kù)(例如集合類(lèi))沒(méi)有使用模板的話,模板將不會(huì)太有用。不過(guò),果真.NET類(lèi)使用了它們,那將有20多種使用.NET類(lèi)的語(yǔ)言不得不也要能和模板一起工作,這在技術(shù)上是非常難以實(shí)現(xiàn)的。 注意到模板(泛型)已經(jīng)被Java社團(tuán)考慮納入Java語(yǔ)言規(guī)范之中是一件有意思的事。或許每個(gè)公司都會(huì)各唱各的調(diào)—Sun說(shuō)“.NET患了最小公分母綜合癥”,而微軟則說(shuō)“Java不支持多語(yǔ)言”。 (8月10日致歉)看了一個(gè)對(duì)Anders Hejlsberg的專訪后(windows.oreilly.com/news/hejlsberg_0800.html" target=_blank>http://windows.oreilly.com/news/hejlsberg_0800.html),感覺(jué)似乎模板已浮出地平線,但第一版沒(méi)有,正因我們上面提到的種種困難。看到IL規(guī)范是如此寫(xiě)法使得IL碼可以展現(xiàn)模板(用一個(gè)非破壞的方式以讓反射可以很好的工作)而字節(jié)碼則不可以是一件很有趣的事。在此,我還給出了一個(gè)關(guān)于Java社團(tuán)考慮要加入泛型的鏈接:http://jcp.org/jsr/detail/014.jsp 】 【譯注:此處是上文提到的對(duì)Anders Hejlsberg采訪的中文版鏈接:http://www.csdn.net/develop/article/11/11580.shtm。另外,如欲了解更多關(guān)于泛型編程知識(shí),請(qǐng)參見(jiàn)此處鏈接:http://www.csdn.net/develop/article/11/11440
|
溫馨提示:喜歡本站的話,請(qǐng)收藏一下本站!