Anders Hejlsberg:是的。4年來,我們一直呆在這個屋子裡。現在,每周一、三、五,我們仍然在這裡會面。
Bruce Eckel:我很想了解一些關於C#設計過程的情況。我直接或間接參與過幾種語言的設計工作,如Python。在Python設計過程中,Guido van Rossum被我們戲稱為“仁慈的獨裁者”。
Anders Hejlsberg:哦,Guido van Rossum就相當於我的位置。
Bruce Eckel:那麼你是C#小組“仁慈的獨裁者”麼?
Anders Hejlsberg:我一般扮演最後拍板者的角色。比如,我們被一個問題困擾多時,到了非解決不可、只能作不二選擇的時候,是由我來作最後決定的。當然大多數這樣的情況下,正確的選擇是顯而易見的。
Bruce Eckel:C#的設計過程是不是和Turbo Pascal、Delphi十分相似?
Anders Hejlsberg:後面兩者的設計過程不是那麼規范的。因為Turbo Pascal主要由我一個人設計,而Delphi也是我和Chuck Jazdzewski、Gary Whizin等幾個為數不多的人來完成,所以沒有必要引入非常規范的設計過程。相反的,C#的設計過程則十分規范,每周一、三、五從1:00到3:00, 我們都會召開一個正式會議,會議議程也相當靈活,所有的問題都會拿到桌面上公開討論、仔細推敲。我們還在互聯網上建立了一個Wiki,這些問題及其解決方案,以及其他一些相關的東西都被發布在上面。
Bruce Eckel:那你們是如何發現這些的問題呢?
Anders Hejlsberg:呵呵,我們有一套行之有效的方法。我們可以通過很多途徑來得到用戶對語言設計的反饋意見——如軟件設計咨詢會、網絡新聞組。這些反饋意見包括:疑問、軟件Bugs、不一致、不規范問題等。這樣我們就能有的放矢了。最後我們將這些問題整理成表,並一一重現它們。對於每個問題,我們都會認真對待,詢問自己:“我們對這個問題有新的想法嗎?真的沒有嗎?這個問題已經擱置好幾個星期了,我們立即花30分鐘集中精力研究一下,看這次是否能有所斬獲。”
Bruce Eckel:那可能一個問題長期沒有解決,都臭不可聞了……
Anders Hejlsberg:也許有些臭問題只有放到下一個版本才能解決了。但是我認為這樣一個過程可以保證不會遺漏任何問題,因為它們都被登記在冊。有時候,你面對這些問題呆坐了很長時間,可能也沒什麼結果。但問題畢竟是被我們逮住了,總有一天會再去“拜訪”它的。也可能不會再去“拜訪”了,但問題終歸是不會被弄丟的。
Anders Hejlsberg:一般而言,好的語言設計過程體現了對設計小組成員的美學品味取向的綜合,也就是你剛才所說的語言美學觀。美學品味帶有極大的主觀性,很難定論,只有產品出來後,你才能仔細體味它。我認為任何程度的可用性研究都不能取代語言美學的價值,因為可用性研究是極有針對性、非常具體的。可能有人問你:“你認為這部分功能如何?”這個問題很難回答。“你對這個語言有什麼看法?”你從何談起呢?你怎麼可能花兩個小時就解決掉所有可用性問題?絕無可能。
Bruce Eckel:人們必須深入理解這個問題。
Anders Hejlsberg:使用一種編程語言會經歷一個感覺微妙變化的過程。只有使用幾個月之後用戶才能真正喜歡上它。他們會逐漸發現:“哦,它給人的感覺很舒服嘛。”你不能急於求成。
開始說過,我們在可用性研究上也做了大量工作,但主要是針對特定的功能。
Bill Venners:可以舉個例子麼?
Anders Hejlsberg:我們將可用性研究的重點放在了IDE功能實現上。我們會問自己:“用戶是否知道在這裡點擊右鍵會有什麼結果?”在純語言功能部分,我們也考慮了一些可用性問題——例如在一些屬性和事件上——不過沒什麼必要,真的。
Bruce Eckel:C#沒有Checked Exceptions,你是怎麼決定是否在C#中放置這種特性的麼?
Anders Hejlsberg:我發現Checked Exceptions在兩個方面有比較大的問題:擴展性和版本控制。我知道你也寫了一些關於Checked Exceptions的東西,並且傾向於我們對這個問題的看法。
Bruce Eckel:我一直認為Checked Exceptions是非常重要的。
Anders Hejlsberg:是的,老實說,它看起來的確相當重要,這個觀點並沒有錯。我也十分贊許Checked Exceptions特性的美妙。但它某些方面的實現會帶來一些問題。例如,從Java中Checked Exceptions的實現途徑來看,我認為它在解決一系列既有問題的同時,付出了帶來一系列新問題的代價。這樣一來,我就搞不清楚Checked Exceptions特性是否可以真的讓我們的生活變得更美妙一些。對此你或許有不同看法。
Bruce Eckel:C#設計小組對Checked Exceptions特性是否有過大量的爭論?
Anders Hejlsberg:不,在這個問題上,我們有著廣泛的共識。C#目前在Checked Exceptions上是保持緘默的。一旦有公認的更好的解決方案,我們會重新考慮,並在適當的地方采用的。我有一個人生信條,那就是——如果你對該問題不具有發言權,也沒辦法推進其解決進程,那麼最好保持沉默和中立,而不應該擺出一個非此即彼的架勢。
Bruce Eckel:極限編程(The Extreme Programmers)上說:“用最簡單的辦法來完成工作。”
Anders Hejlsberg:對呀,愛因斯坦也說過:“盡可能簡單行事。”對於Checked Excpetions特性,我最關心的是它可能給程序員帶來哪些問題。試想一下,當程序員調用一些新編寫的有自己特定的異常拋出句法的API時,程序將變得多麼紛亂和冗長。這時候你會明白Checked Exceptions不是在幫助程序員,反而是在添麻煩。正確的做法是,API的設計者告訴你如何去處理異常而不是讓你自己想破腦袋。
?
2、Checked Exceptions的版本相關性
?
Bill Venners:你提到過Checked Exceptions的擴展性和版本相關性這兩個問題。現在能具體解釋一下它們的意思麼?
Anders Hejlsberg:讓我首先談談版本相關性,這個問題更容易理解。假設我創建了一個方法foo,並聲明它可能拋出A、B、C三個異常。在新版的foo中,我要增加一些功能,由此可能需要拋出異常D。這將產生了一個極具破壞性的改變,因為原來調用此方法時幾乎不可能處理過D異常。
Bill Venners:但即使在沒有Checked Exceptions特性的語言中,(增加新的異常)不是同樣會對程序造成破壞麼?假如新版foo拋出了需要用戶處理的新的異常,難道僅僅因為用戶不希望這個異常發生,他寫代碼時就可以置之不理嗎?
Anders Hejlsberg:不,因為在很多情況下,用戶根本就不關心(異常)。他們不會處理任何異常。其實消息循環中存在一個最終的異常處理者,它會顯示一個對話框提示你程序運行出錯。程序員在任何地方都可以使用try finally來保護自己的代碼,即使運行時發生了異常,程序依然可以正確運行。對於異常本身的處理,事實上,程序員是不關心的。
Bill Venners:如此說來,你認為不要求程序員明確的處理每個異常的做法,在現實中要適用得多了?
Anders Hejlsberg:人們為什麼認為(顯式的)異常處理非常重要呢?這太可笑了。它根本就不重要。在我印象中,一個寫得非常好的程序裡,try finally和try catch語句數目大概是10:1。在C#中,也可以使用和類似try finally的using語句(來處理異常)。
Bill Venners:finally到底干了些什麼?
Anders Hejlsberg:finally保證你不被異常干擾,但它不直接處理異常。異常處理應該放在別的什麼地方。實際上,在任何一個事件驅動的(如現代圖形界面)程序中,在主消息循環裡,都有一個缺省的異常處理過程,程序員只需要處理那些沒被缺省處理的異常。但你必須確保任何異常情況下,原來分配的資源都能被銷毀。這樣一來,你的程序就是可持續運行的。你肯定不希望寫程序時,在100個地方都要處理異常並彈出對話框吧。如果那樣的話,你作修改時就要倒大霉了。異常應該集中處理,並在異常來臨處保護好你的代碼。
?
3、Checked Exceptions的擴展性
?
Bill Venners:那麼Checked Exceptions的擴展性又是如何呢?
Anders Hejlsberg:擴展性有時候和版本性是相關的。 在一個小程序裡,Checked Exceptions顯得蠻迷人的。你可以捕捉FileNotFoundException異常並顯示出來,是不是很有趣?這在調用單個的API時也挺美妙的。但是在開發大系統時,災難就降臨了。你計劃包含4、5個子系統,每個子系統拋出4到10個異常。但是(實際開發時),你每在系統集成的梯子上爬一級,必須被處理的新異常都將呈指數增長。最後,可能每個子系統需要拋出40個異常。將兩個子系統集成時,你將必須寫80個throw語句。最後,可能你都無法控制了。
很多時候,Checked Exceptions都會激怒程序員,於是程序員就想辦法繞過這個特性。他要麼在到處都是寫“throws Exception”,要麼——我都不知道自己看到多少回了——寫“try, da da da da da(譯者注:意思是飛快的寫一段代碼), catch curly curly(譯者注:即‘{ }’)”,然後說:“哦,我會回頭來處理這些空的異常處理語句的。”實際上,理所當然的沒有任何人會回頭干這些事情。這時候,Checked Exceptions已經造成系統質量的極大下降。
Bill Venners:C#和Java傳遞對象事件的方式有所不同。Java使用類(通常是內部類(inner classes),它實現監聽接口(listener interfaces)。C#使用了委托(delegates。譯者注:VJ++6.0就引入了delegates),它有點兒類似函數指針。為什麼要采用委托方式呢?
Anders Hejlsberg:請允許我首先談談對於一般意義上的Simplicity的看法。沒有任何人懷疑簡單的正確性,但是在如何實現簡單的問題上則千差萬別。有一種簡單,我想稱之為Simplexity。你做了一個很實際上復雜的東西,當你將它包裝為一個簡單的東西時,通常是將它的復雜性隱藏起來。所以實際上,你並不是在設計一個真正簡單的系統。這樣的一個包裝過程,從某些角度上看,系統可能被你搞得更復雜了,因為用戶有時候需要知道被隱藏地東西。這就是我說的Simplexity。
Anders Hejlsberg:的確是這樣。它僅僅依賴於信息的兼容性,如參數是否一致。如果是兼容的,你就可以將多個事件放在一起處理。從概念上說,這也完全滿足了用戶對一個回調的結果期望,對吧?只要給我一些參數,我就可以編寫程序了。聽起來很像一個方法吧,那麼我給定該方法的一個引用,這個引用就是我所說的委托了。
Bruce Eckel:最後你也不會丟掉類型檢查。類型檢查是在運行時進行的麼?
Anders Hejlsberg:不,大多數都是在編譯時進行。你創建一個委托的實例後,它就和程序員在C++中使用的成員函數指針差不多。委托指向了某對象的一個方法。如果是虛擬的方法,你也能准確地判斷委托的指向。所以從某種意義上說說,你可以在實例化委托時就解決虛擬問題。通過委托實現的調用可以看作一個間接的(方法)調用指令。
Bruce Eckel:除此之外,就不再需要其他的間接支持了。
Anders Hejlsberg:是的,構造一個委托的時候,你就可以一次性解決虛擬函數表(VTBL)和委托的指向問題;通過委托實現的調用都可以直接准確的得到它對應的方法。所以,委托比接口派遣更有效率,即使和標准的方法派遣相比,它的效率也要高一些。
Bruce Eckel:C#中也有Multicast類型的委托(譯者注:Multicast即多點傳送。是指一個委托可以對應多個方法;委托被調用時,就可以引起多個方法的調用。更詳細的說明可以參考:http://msdn.microsoft.com/vJSharp/productinfo/
Anders Hejlsberg:Multicast是一個徹頭徹尾的orthogonal特性。老實說,在Multicast是否重要這個問題,我也是持保留態度的。我承認它有它的用處,但保守地講的話,我認為所有的委托都是single cast的。有些人覺得Multicast十分重要,使用它有很多優點,但是在大多數情況下委托恰恰都是single cast的。事實上,我們構造的(C#)系統是使用single cast的,只要你(在這個系統裡)不使用Multicast,就不會為它付出什麼代價的。
Bill Venners:委托是怎麼來體現你前面所說的 Simplicity和 Simplexity的呢?它們都體現在哪些地方?
Anders Hejlsberg:如果你仿效接口來實現委托,那麼你最終無可避免地要面對“家務管理”和適配器問題。其實,我們可以觀察任何一個捆綁了JavaBeans的開發工具,它們都會生成一些適配器並告訴你:“你不要修改下面的代碼,我會分發一些非常瘦小的幫助類給你。”這對於我來說就太復雜了。它並不是一個真正簡單的系統,它實際上非常復雜,不過是貌似簡單而已。
?
3、組件概念在C#中的至高地位
?
Bill Venners:O'Reilly網站發布過對於你的一次采訪,當時你這麼評價C#對於屬性和事件的支持:“現在,程序員人員每天都在開發大量的軟件組件。他們不是在開發彼此孤立的應用程序和類庫。每個人都在開發繼承自環境提供的組件的新組件。這些新組件覆蓋了父組件的一些方法、屬性,處理一些事件,然後將這些新組件放回去(到組件庫)。這是一個首先要樹立的概念。”
Anders Hejlsberg:“組件”一詞包含的最重要的意思是組件可以很好的移植。這聽起來十分美妙,但我們可能對此有不同的理解。在一個最簡單的form中,一個組件可能就等同於一個附加了一些數據的類。組件是一個獨立的軟件部件,並不僅僅包含代碼和數據。它是一個通過屬性、方法和事件來實現自我暴露的類;是一個包含了元數據、命名模式等很多附加特征的類。這些特征可以給特定的開發環境提供動態信息,如:組件怎麼使用,組件如何持久化自己的數據。開發環境使用組件的元數據就能夠實現組件功能的智能解釋並給出相應的說明文檔。“組件”包含了如上所述的全部(內容)。
Bill Venners:我使用Java作開發時想到的是,我是在開發類庫而不是組件庫,可能是因為我覺得get/set太笨重了。在激發事件的時候,我也使用get/set,但我沒有打算將這些類拿到集成開發環境中去使用,我一直想象這些類就是給那些那些純編碼的人們使用的。所以我很想知道現在到底有多少人在開發類似JavaBean那樣的組件,面向組件開發是否是未來的趨勢,因為在我的職業生涯中,和組件打的交道太少了。
Anders Hejlsberg:當今,主流的面向對象編程語言實際上都是混血兒。其中有大量的結構化編程,對象基本上也不過是一個包含了一系列方法和一個this指針的結構體。當你想到一個對象或者組件時,我想,從概念來說,你應該意識到它是有屬性和事件的。如果編程語言能給予這些概念頭等待遇的話,理解它就要容易一些。