A Comparative Overview of C#中文版(四)
來源:互聯網
上載者:User
中文 當考慮到C++是怎麼做的時候,Java是幹了件好事,它簡化了參數如何傳遞的問題。在C++中,方法【譯註:C++中沒有方法一說,應該稱為“函數”或“成員函數”】的參數和方法調用通過傳值、引用、指標【譯註:例如int、int*、int&】,使得代碼變得不必要的複雜。C#顯式傳遞引用,不管是方法聲明時還是調用時。它大大地減少了混亂【譯註:這句話應該這麼理解:由於C++的文法問題,有時你並不知道你是在使用一個對象還是一個對象引用,本節後有樣本】,並達到了和Java同樣的目標,但是C#的方式更有表達力。顯然這是C#的主旨—它不把程式員圈在一個圈裡,使他們必須繞一個大彎子才能做成某件事。還記得Java嗎?Java指南裡,建議如何解決傳引用的問題,你應該傳遞一個1個元素的數組去儲存你的值,或另做一個類以儲存這個值。
【譯註:
#include "stdafx.h"
class ParentCls
{
public:
virtual void f(){printf("ParentCls\t");}
};
class ChildCls : public ParentCls
{
public:
virtual void f(){printf("ChildCls\t");}
};
void Test1(ParentCls pc) {pc.f();}
void Test2(ParentCls& pc) {pc.f();}
int main(int argc, char* argv[])
{
ChildCls cc;
Test1(cc);//輸出ParentCls
Test2(cc);//輸出ChildCls
//只看調用處,是不知道你使用的引用還是對象的,但運行結果迥異!
return 0;
}
】
15.特性
C#和Java的編譯代碼裡都包括類似於欄位存取層級的資訊。C#擴充了這個能力,對類中的任何元素,比如類、方法、欄位甚至是獨立參數,你都可以編譯自訂的資訊,並可以於運行時擷取這些資訊。這兒有一個非常簡單的使用特性的類的例子:
[AuthorAttribute ("Ben Albahari")]
class A
{
[Localizable(true)]
public String Text //【譯註:應為public string Text或public System.String Text,如果前面沒有using System的話】
{
get {return text;}
//...
}
}
Java使用一對/** */和@標籤注釋以包含類和方法的附加資訊,但這些資訊(除了@deprecated【譯註:Java1.1版本及以後】)並未build到位元組碼中。C#使用預定義的特性Obsolete特性,編譯器可以警告你,排除廢代碼(就象@deprecated),並用Conditional特性使得可以條件編譯。微軟新的XML庫使用特性來表達欄位如何序列化到XML中,這就意味著你可以很容易地把一個類序列化到XML中,並可以再次重建它。另外一個對特性的恰當的應用是建立真正有威力的類瀏覽工具。C#語言規範詳盡第解釋了怎樣建立和使用特性。
16.switch語句
C#中的switch語句可以使用整型、字元、枚舉或(不象C++或Java)字串。在Java和C++中,如果你在任何一個case語句裡忽略了一個break語句,你就有其它case語句被執行的危險。我想不通為什麼這個很少需要的並容易出錯的行為在Java和C++中都成了預設行為,我也很高興地看到C#不會是這個樣子。
【譯註: 因為C#不支援從一個case標籤貫穿到另一個case標籤。如果需要的話,可以使用goto case或goto default實現】
17.預定義類型
C#基本類型基本上和Java的差不多,除了前者還加入了無符號的類型。C#中有sbyte、byte、short、ushort、int、uint、long、ulong、char、float和double。唯一令人感到驚奇的地方是這兒有一個16個位元組【譯註:原文誤寫為12個位元組】的浮點型數實值型別decimal,它可以充分利用最新的處理器。
【譯註:補充一下,儘管decimal佔用128位,但它的取值範圍比float(32位)、Double(64位)遠遠小得多,但它的精度比後二者的要高得多,可以滿足精度要求極高的財務計算等】
18.欄位修飾符
C#中欄位修飾符基本上Java相同。為了表示不可被修改的欄位,C#使用const和readonly修飾符。const欄位修飾符就象Java的final欄位修飾符,該欄位的實際值被編譯成IL代碼的一部分。唯讀欄位在運行時計算值。對標準C#庫來說,這就可以在不會破壞你的已經部署的代碼的前提下升級。
19.跳躍陳述式
這兒沒有更多的令人驚訝的地方,可能除了臭名卓著的goto語句。然而,這和我們記得的帶來麻煩的20年前的basic的goto語句大不相同。一個goto語句必須指向一個標籤【譯註:goto語句必須必須在該標籤的範圍內,或者換句話說,只允許使用goto語句將控制權傳遞出一個嵌套的範圍,而不能將控制權傳遞進一個嵌套域】或是switch語句裡的一個選擇支【譯註:即所謂的goto case語句】。指向標籤的用法和continue差不多。Java裡的標籤,自由度大一些【譯註:Java中的break和continue語句後可跟標籤】。C#中,goto語句可以指向其範圍的任意一個地方,這個範圍是指同一個方法或finally程式塊【譯註:如果goto語句出現在finally語句塊內,則goto語句的目的地也必須在同一個finally語句塊內】。C#中的continue語句和Java中的基本等價,但C#中不可以指向一個標籤。
【譯註:Java把goto作為保留字,但並未實現它】
20.組合體、名字空間和存取層級
在C#中,你可以把你原始碼中的組件(類、結構、委託、枚舉等)組織到檔案、名字空間和組合體中。
名字空間不過是長類名的文法上的甜言蜜語而已。例如,用不著這麼寫Genamics.WinForms.Grid,你可以如此聲明類Grid並將其包裹起來:
namespace Genamics.WinForms
{
public class Grid
{
//....
}
}
對於使用Grid的類,你可以用using關鍵字匯入【譯註:即using Genamics.WinForms】,而不必用其完整類名Genamics.WinForms.Grid。
組合體是從專案檔編譯出來的exe或dll。.NET運行時使用可配置的特性和版本法則,把它們建立到組合體,這大大簡化了部署—不需要寫註冊表,只要把組合體拷到相關目錄中去即可。組合體還可以形成一個類型邊界,從而解決類名衝突問題。同一組合體的多個版本可以共存於同一進程。每一個檔案都可以包含多個類、多個名字空間。一個名字空間可以橫跨若干個組合體。如此以來,系統將可獲得更大的自由度。
C#中有五種存取層級:private、internal、protected、internal protected和public【譯註:internal protected當然也可以是protected internal,此外再無其它組合】。private和public和Java中意思一樣。C#中,沒有標明存取層級的就是private,而不是包範圍的。internal訪問被局限在組合體中而不是名字空間(這和Java更相似)中。Internal protected等價於Java的protected。protected等價於Java的private protected,而它已被Java廢棄。
21.指標運算
在C#中,指標運算可以被使用在被標為unsafe修飾符的方法裡。當指標指向一個可被垃圾收集的對象的時候,編譯器強迫使用fixed關鍵字去固定對象。這是因為垃圾收集器是靠移動對象來回收記憶體的。但是如果當你使用原始指標時,它所指的對象被移動了,那你的指標將指向垃圾。我認為這兒用unsafe這個關鍵字是個好的選擇—它不鼓勵開發人員使用指標除非他們真的想這麼做。
22.多維陣列
C#可以建立交錯數組【譯註:交錯數組是元素為數組的數組。交錯數組元素的維度和大小可以不同】和多維陣列。交錯數組和Java的數組非常類似。多維陣列使得可以更有效、更準確地表達特定問題。以下是這種數組的一個例子:
int [,,] array = new int [3, 4, 5]; // 建立一個數組
int [1,1,1] = 5;//【譯註:此行代碼有誤:應為array[1,1,1] = 5;】
使用交錯數組:
int [][][] array = new int [3][4][5]; // 【譯註:此行代碼有誤,應為:int [][][] array = new int[3][][];】
int [1][1][1] = 5; 【譯註:此行代碼有誤:應為array[1][1][1] = 5;】【譯註:小心使用交錯數組】
若和結構聯合使用,C#提供的高效率使得數組成為圖形和數學領域的一個好的選擇。
23.構造器和析構器
你可以指定可選的構造器參數:
class Test
{
public Test () : this (0, null) {}
public Test (int x, object o) {}
}
你也可以指定靜態構造器:
class Test
{
static int[] ascendingArray = new int [100];
static Test ()
{
for (int i = 0; i < ascendingArray.Length; i++)
ascendingArray [i] = i;
}
}
析構器的命名採用C++的命名規範,使用~符號。析構器只能應用於參考型別,實值型別不可以,並且不可被重載。析構器不可被顯式調用,這是因為對象的生命期被垃圾收集器所管制。在對象所佔用的記憶體被回收前,對象繼承層次裡的每一個析構器都會被調用。
儘管和C++的命名相似,C#中的析構器更象Java中的finalize方法。這是因為它們都是被垃圾收集器調用而不是顯式地被程式員調用。而且,就象Java的finalize,它們不能保證在各種情況下都肯定被調用(這常常使第一次發現這一點的每一個人都感到震驚)。如果你已習慣於採用確定性析構編程模式(你知道什麼時候對象的析構器被調用),當你轉移到Java或C#時,你必須適應這個不同的編程模型。微軟推薦的和實現的、貫穿於整個.NET架構的是dipose模式。你要為那些需要管理的外部資源(如圖形控制代碼或資料庫連接)的類定義一個dispose()方法。對於分布式編程,.NET架構提供一個約定的基本模型,以改進DCOM的引用計數問題。
24.
受控執行環境
對[C#/IL碼/CLR]和[Java/位元組碼/JVM]進行比較是不可避免的也是正當的。我想,最好的辦法是首先搞清楚為什麼會創造出這些技術來。
用C和C++寫程式,一般是把原始碼編譯成組合語言代碼,它只能運行在特定的處理器和特定的作業系統上。編譯器需要知道目標處理器,因為不同的處理器指令集不同。編譯器也要知道目標作業系統,因為不同的作業系統對諸如如何執行工作以及怎樣實現象記憶體配置這些基本的C/C++的概念不同。C/C++這種模型獲得了巨大的成功(你所使用的大多數軟體可能都是這樣編譯的),但也有其局限性:
l 程式無豐富的介面以和其它程式進行互動(微軟的COM就是為了克服這個限制而建立的)
l 程式不能以跨平台的形式分發
l 不能把程式限制執行在一個安全操作的沙箱裡
為瞭解決這些問題,Java採用了Smalltalk採用過的方式,即編譯成位元組碼,運行在虛擬機器裡。在被編譯前,位元組碼維持程式的基本結構。這就使得Java程式和其它程式進行各種互動成為可能。位元組碼也是機器中立的,這也意味著同樣的class檔案可以運行於不同的平台。最後,Java語言沒有顯式的記憶體操作(通過指標)的事實使得它很適合於編寫“沙箱程式”。
最初的虛擬機器利用解譯器來把位元組碼指令流轉換為機器碼。但是這個過程慢得可怕以致於對於那些關注效能的程式員來說,從來都沒有吸引力。如今,絕大多數JVM都利用JIT編譯器,基本編譯成機器碼—在進入類架構的範圍之前和方法體執行之前。在它運行前,還有可能將Java程式轉換為組合語言,可以避免啟動時間和即時編譯的記憶體負擔。和編譯Visual C++程式相比,這個過程並不需要移去程式對運行時的依賴。Java運行時(這個術語隱藏在術語Java虛擬機器下之下)將處理常式執行的很多至關重要的方面,比如垃圾收集和安全管理。運行時也被認為是受控執行環境。
儘管術語有點含糊不清,儘管從不用解譯器,但.NET基本模型也是使用如上所述方式。.NET的重要的改進將來自於IL自身的設計的改進。Java可以匹敵的唯一方式是修改位元組碼規範以達到嚴格的相容。我不想討論這些改進的細節,這應該留給那些極個別的既瞭解位元組碼也瞭解IL碼的開發人員去討論。99%的象我這樣的開發人員不打算去研究IL代碼規範,這兒列出了一些意欲改進位元組碼的IL設計決策:
l 提供更好的類型中立(有助於實現模板);
l 提供更好的語言中立;
l 執行前永遠都編譯成組合語言,從不解釋;
l 能夠向類、方法等加入附加的聲明性資訊。參見
15.特性;
目前,CLR還提供多作業系統支援,而且在其它領域還提供了對JVM的更好的互用性的支援。參見
26.互用性。
25.庫
語言如果沒有庫那它是沒什麼用的。C#以沒有核心庫著稱,但它利用了.NET架構的庫(它們中的一些就是用C#建立的)。本文著重於講述C#語言的特別之處,而不是.NET的,那應該另文說明。簡單地說,.NET庫包括豐富的線程、集合、XML、ADO+、ASP+、GDI+以及WinForm庫【譯註:現在這些+們多已變成了.NETJ】。有些庫是跨平台的,有些則是依賴於Windows的,請閱讀下一段關於平台支援的討論。
26.互用性
我認為把互用性分成三個部份論述是比較合適的:de,,並且對那些追求語言互用性、平台互用性和標準互用性。Java長於平台互用性,C#長於語言互用性。而在標準互用性方面,二者都各有長短。
(1)
語言互用性
和其它語言整合的能力只存在整合度和難易程度的區別。JVM和CLR都允許你用多種語言寫代碼,只要它們編譯成位元組碼或IL碼即可。然而,.NET平台做了大量的工作—不僅僅是能夠把其它語言寫的代碼編譯成IL碼,它還使得多種語言可以自由共用和擴充彼此的庫。例如,Eiffel或Visual Basic程式員可以匯入C#類,重載其虛方法;C#對象也可以使用Visual Basic方法(多態)。如果你懷疑的話,VB.NET已經被大幅升級,它已具有現代物件導向特性(付出了和VB6相容性的損失)。
為.NET寫的語言一般插入Visual Studio.NET環境中,如果需要的話,可以使用同樣的RAD架構。這就克服了使用其它語言是“二等公民”的印象。
C#提供了P/Invoke【譯註:Platform Invocation Service,平台叫用服務】,這比Java的JNI和C代碼互動起來要簡單得多(不需要dll)。這個特性很象J/direct,後者是微軟Visual J++的一個特性。
(2)
平台互用性
一般而言,這意味著作業系統互用性。但是在過去的幾年裡,internet瀏覽器自身已經越來越象個平台了。
C#代碼運行在一個受控執行環境裡。這是使C#能夠運行在不同作業系統上的技術重要的一步。然而,一些.NET庫是基於Windows的,特別是WinForms庫,它依賴於多如牛毛的Windows API。有個從Windows API移植到Unix系統項目,但目前還沒有啟動,而且微軟也沒有明確的暗示要這麼做。
然而,微軟並沒有忽視平台互用性。.NET庫提供了編寫HTML/DHTML解決方案的擴充能力。對於可以用HTML/DHTML來實現的用戶端來說,C#/.NET是個不錯的選擇。對於跨平台的需要更為複雜的客戶介面的項目,Java是個好的選擇。Kylix—Delphi的一個版本,允許同樣的代碼既可以在Windows上也可以在Linux上編譯,或許將來也會成為跨平台解決方案的一個好的選擇。
(3)
標準互用性
幾乎所有標準,例如資料庫系統、圖形庫、internet協議和對象通訊標準如COM和CORBA,C#都可以訪問。由於微軟在制訂這些大多數標準上擁有權利或發揮了很大的作用,他們對這些標準的支援就處於一個很有利的位置。他們當然會因為商業上的動機(我沒有說他們是否公正)而提供較少的標準支援—對於和他們競爭的東西—比如CORBA(COM的競爭者)和OpenGL(DirectX的競爭者)。類似地,Sun的商業動機(再一次,我沒有說他們是否公正)意味著Java不會盡其所能地支援微軟的標準。
由於C#對象被實現為.NET對象,因此它自動暴露為COM對象。C#因此就既可以暴露COM對象也可以使用COM對象。這樣,就可以整合COM代碼和C#項目。.NET是一個有能力最終替代COM的架構—但是,已經有那麼多已部署的COM組件,我相信,不等.NET取代掉COM,它已經被下一波技術所取代了。但無論如何,希望.NET能有一個長久而有趣的曆史!J
27.結論
到此為止,我希望已給了你一個C#與Java、C++在概念上的比較。總的來說,比起Java,我相信C#提供了更好的表達力並且更適合編寫對效能有嚴格要求的代碼,它也同樣具有Java的優雅和簡單,這也是它們都比C++更具吸引力之處。
—全文完—