ROBERT HESS: 讓我們繼續關注理解.NET體系(Architecture)。我們今天將關注新的C#(念“C Sharp”而不是“C Pound”!)程式設計語言。首先看看MSDN 訊息更新列目。
MSDN 訊息更新
由技術編輯Erica Wiechers主持 (略去與C#無關的談話)
技術閑聊(TECHNO BABBLE )
Robert Hess和資深工程師Anders Hejlsberg會面,要討論…???
ROBERT HESS: 歡迎回到這個欄目。好了,就象任何一個要在其上面開發軟體的平台,為了使這些應用軟體能真正地運行起來,所需要的一個主要東西就是一種程式設計語言。目前,.NET已經擁有了一種專門為此開發的專用語言,我們稱之為C#。今天和我們在一起的是Anders Hejlsberg,他是微軟一位資深的工程師,在C#語言以及.NET平台領域都扮演了一個關鍵的角色,在以前的“show”節目裡大家都見過他。所以我邀請他到這裡來,和大家一起談論C#,談談它是什麼,有了它程式員就可以從中得到什麼好處,以及它是怎樣真正地影響到應用程式的開發的。
那麼,您究竟從事C#哪方面的研究工作,是從什麼時候開始的?
ANDERS HEJLSBERG: 在最近大概兩年半的時間裡,我們一直在從事C#的研究工作。設計小組由4人組成,而且在這段時間裡,我把主要的精力都放在了這上面。要知道,對於一種新的程式設計語言,我們有許多事情要去做。我想首先也許就是為程式員提供方便,使程式員開發出更多的產品就是最終目標。如今,雖然在產品的收益方面僅僅只是初具規模,但可以說,我們的目標,在某種意義上,不但要使C#具有C++強大的功能和出色的表現,而且還要使其具備簡單易用以及RAD(Rapid Application Development,快速的應用程式開發)語言的生產率。
我們已經做了一些工作,例如,涉及到程式員如何利用更好的工具編寫組件的範疇。如果你看看我們是怎樣寫應用程式的,應該是
曾經怎樣寫應用程式的,如果回顧過去,也就是說5年或10年以前,應用程式就是這樣鐵板一塊地被建立的,它和作業系統的唯一互動,你知道,就是進行檔案的i/o操作,而且無論如何作業系統都會載入應用程式,接著使用者就與其互動,最後退出。看看現在我們是如何建立internet應用程式的,相比起來簡直是天壤之別。應用程式不再是鐵板一塊了,而是由一系列更小的組件構成,它們分別棲息於不同的環境。例如,你可能擁有類似預存程序(stored procedures)和SQL Server那樣的組件,也可能擁有棲息於瀏覽器中的控制項,或者是存在於ASP網頁中的代碼。商業對象生存於中介層,調用整個集合的組件就等於調用該應用程式。於是為了使……
ROBERT HESS: 而當時您明白其中的每一個組件都比5或10年前的一個應用程式更加複雜。
ANDERS HEJLSBERG: 噢,絕對,絕對。同樣,為了減少建立組件的複雜性,不象那種大塊頭程式,每當必須建立其中的一個組件時,就不必另起爐灶,而應該盡量繼承一些早已存在於這些專門的棲息環境(hosting environment)中的東西。如果要寫瀏覽器中的一個控制項,就要繼承一個基控制項;如果要寫中介層的一個商業對象,就要繼承某些商業對象類;而要想公開屬性、方法和事件等等,就要說明它們是如何通過把屬性與組件聯絡一起,以便與棲息環境相結合。而且還要能夠為這些組件撰寫隨組件一起發布的文檔。
ROBERT HESS: 所有的這些只是標準的物件導向的編程,Smalltalk
擁有此特性的時間似乎已經不短了,並且……
ANDERS HEJLSBERG: 嗯,絕對如此,現在並不是不能做這樣的事。但如果注意一下這種今天已經得到了廣泛應用的程式設計語言,它們實際上並不真正支援面向組件的概念。如果你當初同意我們什麼時候談論組件,那麼認為它們具有屬性、方法和事件在當今是極其平常的。但如果注意一下C++,就會發現它僅僅提到“方法”這個概念,沒有屬性,也沒有事件。現在,你可以利用命名模式來模擬它們,也就是說,對於一個屬性,可以用一個get color和一個set color的方法來取代一個color屬性;而要代替類中作為第一個類成員的事件,你擁有的用於接收事件的介面就應該被實現,為了這樣,你就必須處理一些瑣碎的事……
ROBERT HESS:嗯,部分原因是由於C++是基於C的,它只是象預先處理程那樣,又由於C本來就不支援,C++也不支援……
ANDERS HEJLSBERG: 是的,實際上我多少認為,你所看到的就是從C到C++再到C#的發展過程。從C到C++,物件導向編程的概念被加進去了。如果你經曆了從C++到C#,那麼我會說,面向組件編程的概念也已經被加進去了,而它們之間真的存在著某些相似的地方。就象可以用C代替C++進行物件導向的編程,你也可以用C++代替C#進行面向組件的編程,只不過是更困難而已。用C來進行物件導向的編程是極其困難的,你必須手工地布置V表(虛擬函數表),並且還要處理所有瑣碎的事情。用C++寫組件也確實可行,但必須要為屬性手工地設定命名模式(naming patterns),必須實現事件同步(event syncs),必須具有外部的IDL檔案,在檔案中可以對棲息屬性(hosting attributes)進行描述,必須具有外部的文檔檔案,等等。我們只真正採納了其次一個合理的步驟,它反映了人們是如何編寫應用程式並把其整合到語言中的
ROBERT HESS: 那麼,最初的一些目標是什嗎?僅出自內心的看法,當您第一次著手這個項目時,您要解決的問題和要確定的這種新語言的方向是什嗎?
ANDERS HEJLSBERG: 嗯,我認為正如您所說,面向組件只是一方面。我想另一個關鍵的因素就是簡單化。使編寫應用程式更加簡單化,不讓程式員做一些瑣碎的事,機器可以代你做。大量的簡化取決於.NET runtime本身,但也取決於語言。基本上,最終我們所做的,就是讓你把更多的時間、更多的精力放在演算法上,而讓系統去做一些瑣碎的事。我認為,許多其它非常關鍵的事情比較現實,我總不能吩咐人們把他們現有的代碼通通扔掉吧。我們必須找到權衡的方法,並不僅僅是你的編程技巧,而且還包括你以前編寫的、早已存在的代碼。因此在C#裡,以權衡你技巧的名義,我們努力堅持在與C++的基礎文法最接近、最真實的地方。所以用了C#,C++程式員會立即覺得很眼熟、很親切。
ROBERT HESS: 您的所做的一切應當以C語言為基礎,是否就是當初的思路之一?或者您是否原來就認為,讓我們應與過去徹底決裂,再開始設計一種全新的語言?
ANDERS HEJLSBERG: 嗯,我想這種語言應該以一種嶄新的面貌出現,可是我們明白,必須讓C和C++程式員熟悉這種語言。當然那就意味著在某種程度上,我們必須把語句結構從彎曲的大括弧變換成其它一些東西。我們已經打下了一些基礎,但仍然存在著其它某些關鍵的規則,譬如允許寫健壯的軟體,這就意味著象記憶體回收(garbage collection)、異常處理、型別安全這樣的功能,根本地改變了設計該語言的思路,非常難於上手,以後也不容易擴充。我的意思是說,在C++中,C++語言擁有的一個強大功能之一,但有時也同樣是痛點之一,事實上您知道,就是沒有型別安全。如果你瞭解清楚自己要做什麼,就會從中擷取巨大的力量,否則,只是自找苦吃。在C++中,獲得一個虛懸指標(dangling pointer)易如反掌。同樣,覆蓋掉一個數組的尾部,或者擁有一個未初始化的變數等等,也是極其容易事情。而我們需要解決這些難題。我認為,我們不能只從 C++著手並擴充它。而真的必須以退為進,以C++的靈魂設計出新的方案,而這些我們已經差不多完成了。
ROBERT HESS: 那麼其它語言呢?您注意到這些語言在做了什麼嗎?是否它們就是Pascal 或 Modular 2 或 FORTH,您從中得到什麼借鑒?
ANDERS HEJLSBERG: 絕對!噢,我們考慮到,嗯,我的意思是說,我本人出身於深厚的Pascal背景,所以,自然要考慮到Pascal, Modula, Oberon, 要考慮到Smalltalk,Java,C++以及所有的語言。要知道,今天它們能生存下來並得到了應用,且或多或少地傳播開了。
ROBERT HESS: 您覺得這些語言的哪些功能比C和C++表現得更出色?而您就可在新的語言中引進這些功能?
ANDERS HEJLSBERG: 嗯,我認為有一件事可以說明,我總是喜歡Smalltalk,就是因為在該語言中,任何東西都是對象。這使程式得到了大大的簡化,因為無論擁有什麼樣的資料片,都可以把它當作一個對象從A點搬到B點。一般來說,任何東西都可以對其進行操作。可以把它當作物件類型,放在容器中。在Smalltalk實際的實現過程中,如果這樣做的話,程式效能的額外負擔(overhead)就會大大加重。例如,在Smalltalk中,當運算float數字時,每產生一個新的數字,如當1.0和2.0加到一起時,就要分配一個含有3.0值的新對象。而這樣做,代價當然是非常昂貴的。如今在C#中,我們已經進行了一些革新工作,使你獲得了同一樣的利益,而卻沒有額外負擔。只要把float當成float,或者是double就把它當成double使用,就沒有什麼代價。但如果把它們視為對象(也僅當這樣做時),就得給它們分配堆(heap)。因此,就形成了樣相當完美的統一體,既能給您大量的好處,而又沒有程式效能的額外負擔。
ROBERT HESS:C#產生的最終結果的某些結構到底如何?您得到了一個C#程式的文字檔並且編譯它,那麼編譯器本身存在著些什麼問題,如何設計這些方案以便在使用時更具效率,而這種最佳化其它語言有可能已經做過,而甚至最終還要產生二進可執行檔嗎?
ANDERS HEJLSBERG:嗯,我們已經做了一些涉及到如何編碼的工作。假如你是一個C++程式員,當然熟悉怎麼做,在C++中聲明和實現是分開的。所以所有聲明都放在H檔案裡,接著在另一些模組中把它包含進去(# include),然後再在CPP檔案中寫出實現程式。在C#中,把它們都寫在同一個地方。這樣,可在那裡寫出聲明接著又立即寫出實現代碼。
ROBERT HESS:假如您必須在另外一些檔案中採用在main檔案中聲明的某些值,那又怎麼辦?
ANDERS HEJLSBERG:那麼會出現什麼情況呢?當編譯時間,實際產生的程式碼或重建的輸出檔案都含有兩種代碼:中繼資料(metadata),符號表(symbol tables)或其它相關的符號資訊(symbolic information),而不是僅產生只含有可執行代碼的X86機器碼。在某種意義上,這些代碼變成了自我描述。所以當想從一個代碼片使用另一代碼片時,只要引用它們便可,而這些代碼的自我描述足以讓人們知道在那裡有什麼類,類擁有什麼成員,可以調用什麼方法,屬性是什麼以及類型名是什麼,等等。
ROBERT HESS:那麼,是不是象您所指的.OBJ檔案或.EXE檔案,抑或……
ANDERS HEJLSBERG:嗯,您說的很對,我們在.NET中使用的格式就是PE格式,故可以指向另外的EXE或DLL。我們現在調用這些組譯工具,並基本上用這種語言大量地描述這些進階DLL,它們不僅含有代碼,也含有這樣的資訊:說明什麼在代碼中,以及這些代碼真正地引用了其它什麼樣的組譯工具。
ROBERT HESS:您所說的代碼是指二進位代碼或可執行代碼。
ANDERS HEJLSBERG:嗯,實際上,我們並不直接產生可執行檔X86機器碼,而是產生MSIL,也就是中繼語言,它由.NET定義,並由JIT(Just-In-Time 編譯器)編譯。
ROBERT HESS: Ok,這樣看來,您擁有了一個可執行檔,其本身與中繼語言以及中繼資料相關聯,而如果我想要在其中一個應用程式裡使用它,我只要指向它並說道,嘿,我要借用這些類,借用這些對象,並在這樣的程式中使用它。這使我想起了許多人曾經提過的一個問題,涉及到中繼語言存在著潛在的漏洞,有些人可能會攫取這種檔案並對其進行反編譯,然後再恢複成當初的原始碼。因此,並非所有的智能特性(intellectual property)都有利於開發人員。這方面還有什麼問題?
ANDERS HEJLSBERG:嗯,首先,您現在實際上可以用DLL進行處理。這可能有點難,但您可以採用包含X86碼的一個DLL,然後至少把它反編譯成組譯工具。您可做同樣的……
ROBERT HESS:在我的Apple II上,我一直都是這樣做的。
ANDERS HEJLSBERG: 確實如此,我對此感到慚愧。但是運用.NET DLL並把其反編譯成MSIL,也可以做相同的工作。它們並不能直接地被反編譯成C#,儘管您也有可能蒙對,但太難了。不同的地方在於,有著非常多的符號資訊(symbolic information)關聯到由C#產生的程式碼,以及MSIL,對不起,是.NET組譯工具。例如,你可以從代碼中得知,什麼類在這裡,它們的成員是什麼,等等。這是一個需要解決的棘手問題,因為讓代碼自我描述(self describing)的好處多多,但事實上代碼對自己進行的描述,也同樣使反編譯工具所處理的代碼變得似乎容易理解了。如果我們認識到這個問題,基本上我們要做的,就是建立一個稱作攪拌機(obfuscator)的東西,它會加入並搞亂代碼,使代碼變得幾乎難於看懂,可仍然保留著原來相同的公用介面。
ROBERT HESS:是的,因為您遇到這樣的難題:當採用、編寫這些編譯器程式時,就想讓編譯器理解代碼(或類似的過程等),但卻不想讓人們在同一層次上看懂它們。
ANDERS HEJLSBERG:確實如此,確實如此。我確實想指出,對於一個小程式,實際上可以對其進行反編譯,給出足夠的時間和資源,你甚至可理解它在幹什麼。然而對於真實世界中的一個應用程式,這卻是一項艱巨的任務。在現實中你最好運行這個應用程式,理解它在幹什麼,然後寫出它的一個copy。你會很快達到這個水平的。
ROBERT HESS:或許寫出一個更牛的程式,因為您是一個更牛的程式員,對嗎?為了斷定我們的觀眾是否想要著手實現下一個C#項目,他們需要去理解有關C#的一些什麼問題?
ANDERS HEJLSBERG:嗯,我想首先你必須考慮到自己的基礎。如果你有現成的代碼體,讓我們假定它是用C++寫成的吧,也許把這些代碼移植到.NET framework的最佳途徑就是使用Managed C++,該C++編譯器隨.NET一起發布。然而,如果你著眼於編寫新的代碼,也就是寫新模組、更大的模組,加入到一個應用程式或一個完全瞭解的應用程式中,而且你精通C++,那麼我建議考慮C#。
ROBERT HESS:因此,我們沒有必要說,每個人都必須用C#重寫他們的應用程式。我們在說,人們必須瞭解他們目前所從事的工程的類型,是否是一個現成的項目、陳舊代碼,並且有時還要用C#寫一些組件,莫非您可以交替地使用C#和C++?
ANDERS HEJLSBERG:噢,絕對。首先,如果你擁有現成的代碼,假定這些代碼是由Windows平台所支援的任何一種語言寫成的,則把它們編譯成COM組件或DLL,就會獲得與這些代碼很強的互動操作能力(interoperability)。如果要寫專門用於.NET framework的代碼,用於.NET framework的新代碼,當然要用.NET framework所支援的任何語言寫。我們計劃在Visual Studio .NET中發布四種語言:C#、 C++、 Visual Basic 和 Jscript。可是為了與產業界和學術界合作,我認為,最新的統計可能不是很準確,但我想總共大約17種不同的語言現在都瞄向了這一個平台,其範圍一直從APL到Cobol。
ROBERT HESS:那麼象Fortran又怎麼樣呢?
ANDERS HEJLSBERG:我相信相關的工作進行中中,可我不能準確地知道到底誰正在設計Fortran編譯器。但關鍵的是,實際上我們已經做了多次示範了,你可以在C#中寫一個基類,並在C++中繼承它,然後再用VB程式建立它的一個實體。在不同的語言之間進行的互用操作是無縫的。我想正是這些效能,使.NET framework甩開產業界中其它競爭者的產品。(有興趣的讀者可以到http://www.lahey.com/net_down.htm去下載
Lahey/Fujitsu Fortran for .NET Technology Preview 1,譯註)
ROBERT HESS:而且它採用並允許在基礎層次上進行多語言的互用操作。
ANDERS HEJLSBERG:是的,很準確。不過是位於一個很高的層次。您可能會爭辯:現在的語言可以進行互用操作,但只能位於很低的層次,例如,DLL的進入點、具有指標在其中的結構,等等。而我們正在談論到更高的語義層次,是位於物件導向的層次,具有類和介面等等。