很多人說C#是微軟用來和Java抗衡的武器,因?yàn)槎咴诤艽蟪潭壬嫌兄@人的相似
,盡管如此,兩者不同的地方也很多,所謂“于細(xì)微處見差異”。那么兩者的相似和區(qū)
別都在什么地方呢?我們從今天開始,會(huì)從各個(gè)角度來對比C#和Java的特點(diǎn),希望能對
正在學(xué)習(xí)、使用C#的朋友有所幫助。
1、C#和.NET平臺(tái)的概貌
2000年6月,微軟發(fā)布C#語言和.NET平臺(tái)。C#語言是一種強(qiáng)類型的,面向?qū)ο蟮恼Z言
,它具有語法簡單、表達(dá)力強(qiáng)的特點(diǎn),而.NET平臺(tái)則是構(gòu)成微軟的“.NET計(jì)劃”的基石
。
.NET平臺(tái)的核心包括兩方面,一方面就是著名的通用語言運(yùn)行機(jī)(Common Language
Runtime),雖然這個(gè)名詞起得晦澀了點(diǎn),不過大家可以拿它和Java的虛擬機(jī)來作比較,
二者完成的任務(wù)大致相同;另一方面就是一大堆通用函數(shù)庫,這些庫函數(shù)可以被多種語
言調(diào)用,并且通過編譯都產(chǎn)生一種共同的中間語言(Intermediate Language),這種語
言也可以拿Java的字節(jié)碼來類比,雖然完成的方式有些不一樣。
2、C#和Java
下面簡單地把C#和Java的相似處列出來,雖然在這里我們重點(diǎn)討論的是C#和Java的
不同點(diǎn),但是了解一下二者的相同之處也是很有必要的。
二者都編譯成跨平臺(tái)的、跨語言的代碼,并且代碼只能在一個(gè)受控制的環(huán)境中運(yùn)行
自動(dòng)回收垃圾內(nèi)存,并且消除了指針(在C#中可以使用指針,不過必須注明unsafe
關(guān)鍵字)
都不需要頭文件,所有的代碼都被“包(package)”限制在某個(gè)范圍內(nèi),并且因?yàn)闆]
有頭文件,所以消除了類定義的循環(huán)依賴
所有的類都是從對象派生出來,并且必須使用New關(guān)鍵字分配內(nèi)存
用對象加鎖的方式來支持多線程
都具有接口(interface)的概念
內(nèi)部類
繼承類的時(shí)候不會(huì)以某種特定的訪問權(quán)限來繼承;
沒有全局函數(shù)或者常量,一切必須屬于類;
數(shù)組或者字符串都自帶長度計(jì)算和邊界檢查;
只使用“.”操作符,沒有“->”和“::”;
“null”、“boolean”和“bool”成為了關(guān)鍵字;
任何變量均在使用前進(jìn)行初始化;
不能使用整數(shù)來返回到if條件語句中,必須使用布爾值;
“Try”模塊后可以有“finally” ;
3. 屬性(Property)
屬性的概念對大家來說應(yīng)該是很熟悉的,類成員函數(shù)可以自由地訪問本類中的任何
屬性成員。不過若要從一個(gè)類中去訪問另一個(gè)類中的屬性,那就比較麻煩了,所以很多
時(shí)候我們使用Getxxx和Setxxx方法,這樣看起來顯得極不自然,比如用Java或者C++,代
碼是這樣的:
foo.setSize (getSize () + 1);
label.getFont().setBold (true);
但是,在C#中,這樣的方法被“屬性化”了。同樣的代碼,在C#就變成了:
foo.size++;
label.font.bold = true;
可以看出來,C#顯然更容易閱讀和理解。我們從這個(gè)“屬性方法”的子程序代碼中
,也可以看到類似情況:
Java/C++:
public int getSize()
{
return size;
}
public void setSize (int value)
{
size = value;
}
C#:
public int Size
{
get{return size;}
set{size = value;}
}
為了區(qū)分這種屬性化的方法和類的屬性成員,在C#中把屬性成員稱作“域(field)”
,而“屬性”則成為這種“屬性化的方法”專用的名詞。順便說一句,其實(shí)這樣的屬性
化方法在VB和DELPHI中是經(jīng)常碰到的,在VB中它也就叫屬性。
另外,在C#中Get和Set必須成對出現(xiàn),一種屬性不能只有Get而沒有Set(在Java和
C++中就可以只有Get或者只有Set),C#中這樣做的好處在于便于維護(hù),假如要對某種屬
性進(jìn)行修改,就會(huì)同時(shí)注意Get和Set方法,同時(shí)修改,不會(huì)改了這個(gè)忘了那個(gè)。
4、對象索引機(jī)制(Indexer)
C#中引入了對象索引機(jī)制。說得明白點(diǎn),對象索引其實(shí)就是對象數(shù)組。這里和上一
節(jié)中的屬性聯(lián)系起來講一下,屬性需要隱藏Get和Set方法,而在索引機(jī)制中,各個(gè)對象
的Get或者Set方法是暴露出來的。比如下面的例子就比較清楚地說明了這一點(diǎn)。
public class Skyscraper
{
Story[] stories;
public Story this [int index] {
get {
return stories [index];
}
set {
if (value != null) {
stories [index] = value;
}
}
}
...
}
5. 指代(Delegate)
指代這個(gè)玩意很特別,它有點(diǎn)象指針,但又不完全是,不過大家還是可以把它理解
為一種類型安全的、面向?qū)ο蟮闹羔槨#ㄊ裁词穷愋桶踩兔嫦驅(qū)ο缶筒挥弥v了吧?)
順便提一句,有很多書上把Delegate翻譯成代理,我覺得這樣翻不夠確切,翻譯成“指
代”更恰當(dāng)些,道理上吻合,并且還符合它的本來意思——微軟本來就是用Delegate來
“取代指針”,所以叫“指代”,呵呵。
說起指代,也許至今Sun還會(huì)對它憤憤不已,為什么呢?因?yàn)樵赟un的標(biāo)準(zhǔn)Java中是
沒有這個(gè)東西的,它是微軟99年發(fā)布的MSVJ++6添加的“新特性”。為此,兩家公司吵得
不亦樂乎,并且還專門在網(wǎng)上寫了大量文章互相攻擊,有興趣的朋友可以去看看(只有
英文版)。
http://www.Javasoft.com/docs/white/delegates.html
http://msdn.microsoft.com/visualj/technical/articles/delegates/truth.asp
話歸正傳,指代有什么特點(diǎn)呢?一個(gè)明顯的特點(diǎn)就是它具有了指針的行為,就好象
從Java又倒回到了C++。在C#中,指代完成的功能大概和C++里面的指針,以及Java中的
接口相當(dāng)。但是,指代比起C++的“正宗指針”來又要高明一些,因?yàn)樗梢酝瑫r(shí)擁有多
個(gè)方法,相當(dāng)于C++里面的指針能同時(shí)指向多個(gè)函數(shù),并且是類型安全的,這一點(diǎn)體現(xiàn)了
它的“對象”特性;而比起Java的接口來,指代高明的地方在于它能可以不經(jīng)過內(nèi)部類
就調(diào)用函數(shù),或者用少量代碼就能調(diào)用多種函數(shù),這一點(diǎn)體現(xiàn)了它的“指針”特性。呵
呵,很有“波粒二象性”的味道吧?指代最重要的應(yīng)用在于對于事件的處理,下一節(jié)我
們將重點(diǎn)介紹。
6、事件(Event)
C#對事件是直接支持的(這個(gè)特點(diǎn)也是MSVJ所具有的)。當(dāng)前很多主流程序語言處
理事件的方式各不相同,Delphi采用的是函數(shù)指針(這在Delphi中的術(shù)語是“closure”
)、Java用改編類來實(shí)現(xiàn)、VC用WindowsAPI的消息系統(tǒng),而C#則直接使用delegate和ev
ent關(guān)鍵字來解決這個(gè)問題。下面讓我們來看一個(gè)例子,例子中會(huì)給大家舉出聲明、調(diào)用
和處理事件的全過程。
//首先是指代的聲明,它定義了喚醒某個(gè)函數(shù)的事件信號
public delegate void ScoreChangeEventHandler (int newScore, ref bool cancel)
;
//定義一個(gè)產(chǎn)生事件的類
public class Game
{
// 注意這里使用了event關(guān)鍵字
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)
{
// 裁判負(fù)責(zé)調(diào)整比賽中的分?jǐn)?shù)變化
game.ScoreChange += new ScoreChangeEventHandler (game_ScoreChange);
}
// 注意這里的函數(shù)是怎樣和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!");
}
}
}
// 主函數(shù)類,用于測試上述特性
public class GameTest
{
public static void Main ()
{
Game game = new Game ();
Referee referee = new Referee (game);
game.Score = 70;
game.Score = 110;
}
}
在主函數(shù)中,我們創(chuàng)建了一個(gè)game對象和一個(gè)裁判對象,然后我們通過改變比賽分
數(shù),來觀察裁判對此會(huì)有什么響應(yīng)。
請注意,我們的這個(gè)系統(tǒng)中,Game對象是感覺不到裁判對象的存在的,Game對象在
這里只負(fù)責(zé)產(chǎn)生事件,至于有誰會(huì)來傾聽這個(gè)事件,并為之作出反應(yīng),Game對象是不作
任何表態(tài)的。
我們注意到,在裁判類的Referee函數(shù)中,Game.ScoreChange后面使用了+=和-=操作
符,這是什么意思呢?回到我們定義ScoreChange的地方,可以發(fā)現(xiàn)ScoreChange是用ev
ent關(guān)鍵字修飾的,那么這里的意思就很明白了:ScoreChange是一個(gè)事件,而事件被觸
發(fā)后需要相應(yīng)的事件處理機(jī)制,+=/-=就是為這個(gè)事件增加/移除相對應(yīng)的事件處理程序
,而且,并不是一個(gè)事件只能對應(yīng)一個(gè)處理程序,我們還可以用這兩個(gè)操作符為同一事
件增加/移除數(shù)個(gè)事件處理程序。怎么樣?很方便吧!
在實(shí)際應(yīng)用中,和我們上面講的(競賽-裁判)機(jī)制很相近的系統(tǒng)就是圖形用戶界面
系統(tǒng)了。Game對象可以看作是圖形界面上的小零件,而得分事件就相當(dāng)于用戶輸入事件
,而裁判就相當(dāng)于相應(yīng)的應(yīng)用程序,用于處理用戶輸入。
指代機(jī)制的首次亮相是在MSVJ里,它是由Anders Hejlsberg發(fā)明的,現(xiàn)在又用到了
C#中。指代用在Java語言中的后果,則直接導(dǎo)致了微軟和Sun之間對類和指針的關(guān)系產(chǎn)生
了大量的爭論和探討。有意思的是,Java的發(fā)明者James Gosling非常幽默地稱呼指代的
發(fā)明者Anders Hejlsberg為“‘函數(shù)指針’先生”,因?yàn)锳nders Hejlsberg總是想方設(shè)
法地把指針變相地往各種語言中放;不過有人在看了Java中大量地使用了各種類后,也
戲稱Java的發(fā)明者James Gosling為“‘全都是類’先生”,真是其中滋味,盡在不言中
啊。
,盡管如此,兩者不同的地方也很多,所謂“于細(xì)微處見差異”。那么兩者的相似和區(qū)
別都在什么地方呢?我們從今天開始,會(huì)從各個(gè)角度來對比C#和Java的特點(diǎn),希望能對
正在學(xué)習(xí)、使用C#的朋友有所幫助。
1、C#和.NET平臺(tái)的概貌
2000年6月,微軟發(fā)布C#語言和.NET平臺(tái)。C#語言是一種強(qiáng)類型的,面向?qū)ο蟮恼Z言
,它具有語法簡單、表達(dá)力強(qiáng)的特點(diǎn),而.NET平臺(tái)則是構(gòu)成微軟的“.NET計(jì)劃”的基石
。
.NET平臺(tái)的核心包括兩方面,一方面就是著名的通用語言運(yùn)行機(jī)(Common Language
Runtime),雖然這個(gè)名詞起得晦澀了點(diǎn),不過大家可以拿它和Java的虛擬機(jī)來作比較,
二者完成的任務(wù)大致相同;另一方面就是一大堆通用函數(shù)庫,這些庫函數(shù)可以被多種語
言調(diào)用,并且通過編譯都產(chǎn)生一種共同的中間語言(Intermediate Language),這種語
言也可以拿Java的字節(jié)碼來類比,雖然完成的方式有些不一樣。
2、C#和Java
下面簡單地把C#和Java的相似處列出來,雖然在這里我們重點(diǎn)討論的是C#和Java的
不同點(diǎn),但是了解一下二者的相同之處也是很有必要的。
二者都編譯成跨平臺(tái)的、跨語言的代碼,并且代碼只能在一個(gè)受控制的環(huán)境中運(yùn)行
自動(dòng)回收垃圾內(nèi)存,并且消除了指針(在C#中可以使用指針,不過必須注明unsafe
關(guān)鍵字)
都不需要頭文件,所有的代碼都被“包(package)”限制在某個(gè)范圍內(nèi),并且因?yàn)闆]
有頭文件,所以消除了類定義的循環(huán)依賴
所有的類都是從對象派生出來,并且必須使用New關(guān)鍵字分配內(nèi)存
用對象加鎖的方式來支持多線程
都具有接口(interface)的概念
內(nèi)部類
繼承類的時(shí)候不會(huì)以某種特定的訪問權(quán)限來繼承;
沒有全局函數(shù)或者常量,一切必須屬于類;
數(shù)組或者字符串都自帶長度計(jì)算和邊界檢查;
只使用“.”操作符,沒有“->”和“::”;
“null”、“boolean”和“bool”成為了關(guān)鍵字;
任何變量均在使用前進(jìn)行初始化;
不能使用整數(shù)來返回到if條件語句中,必須使用布爾值;
“Try”模塊后可以有“finally” ;
3. 屬性(Property)
屬性的概念對大家來說應(yīng)該是很熟悉的,類成員函數(shù)可以自由地訪問本類中的任何
屬性成員。不過若要從一個(gè)類中去訪問另一個(gè)類中的屬性,那就比較麻煩了,所以很多
時(shí)候我們使用Getxxx和Setxxx方法,這樣看起來顯得極不自然,比如用Java或者C++,代
碼是這樣的:
foo.setSize (getSize () + 1);
label.getFont().setBold (true);
但是,在C#中,這樣的方法被“屬性化”了。同樣的代碼,在C#就變成了:
foo.size++;
label.font.bold = true;
可以看出來,C#顯然更容易閱讀和理解。我們從這個(gè)“屬性方法”的子程序代碼中
,也可以看到類似情況:
Java/C++:
public int getSize()
{
return size;
}
public void setSize (int value)
{
size = value;
}
C#:
public int Size
{
get{return size;}
set{size = value;}
}
為了區(qū)分這種屬性化的方法和類的屬性成員,在C#中把屬性成員稱作“域(field)”
,而“屬性”則成為這種“屬性化的方法”專用的名詞。順便說一句,其實(shí)這樣的屬性
化方法在VB和DELPHI中是經(jīng)常碰到的,在VB中它也就叫屬性。
另外,在C#中Get和Set必須成對出現(xiàn),一種屬性不能只有Get而沒有Set(在Java和
C++中就可以只有Get或者只有Set),C#中這樣做的好處在于便于維護(hù),假如要對某種屬
性進(jìn)行修改,就會(huì)同時(shí)注意Get和Set方法,同時(shí)修改,不會(huì)改了這個(gè)忘了那個(gè)。
4、對象索引機(jī)制(Indexer)
C#中引入了對象索引機(jī)制。說得明白點(diǎn),對象索引其實(shí)就是對象數(shù)組。這里和上一
節(jié)中的屬性聯(lián)系起來講一下,屬性需要隱藏Get和Set方法,而在索引機(jī)制中,各個(gè)對象
的Get或者Set方法是暴露出來的。比如下面的例子就比較清楚地說明了這一點(diǎn)。
public class Skyscraper
{
Story[] stories;
public Story this [int index] {
get {
return stories [index];
}
set {
if (value != null) {
stories [index] = value;
}
}
}
...
}
5. 指代(Delegate)
指代這個(gè)玩意很特別,它有點(diǎn)象指針,但又不完全是,不過大家還是可以把它理解
為一種類型安全的、面向?qū)ο蟮闹羔槨#ㄊ裁词穷愋桶踩兔嫦驅(qū)ο缶筒挥弥v了吧?)
順便提一句,有很多書上把Delegate翻譯成代理,我覺得這樣翻不夠確切,翻譯成“指
代”更恰當(dāng)些,道理上吻合,并且還符合它的本來意思——微軟本來就是用Delegate來
“取代指針”,所以叫“指代”,呵呵。
說起指代,也許至今Sun還會(huì)對它憤憤不已,為什么呢?因?yàn)樵赟un的標(biāo)準(zhǔn)Java中是
沒有這個(gè)東西的,它是微軟99年發(fā)布的MSVJ++6添加的“新特性”。為此,兩家公司吵得
不亦樂乎,并且還專門在網(wǎng)上寫了大量文章互相攻擊,有興趣的朋友可以去看看(只有
英文版)。
http://www.Javasoft.com/docs/white/delegates.html
http://msdn.microsoft.com/visualj/technical/articles/delegates/truth.asp
話歸正傳,指代有什么特點(diǎn)呢?一個(gè)明顯的特點(diǎn)就是它具有了指針的行為,就好象
從Java又倒回到了C++。在C#中,指代完成的功能大概和C++里面的指針,以及Java中的
接口相當(dāng)。但是,指代比起C++的“正宗指針”來又要高明一些,因?yàn)樗梢酝瑫r(shí)擁有多
個(gè)方法,相當(dāng)于C++里面的指針能同時(shí)指向多個(gè)函數(shù),并且是類型安全的,這一點(diǎn)體現(xiàn)了
它的“對象”特性;而比起Java的接口來,指代高明的地方在于它能可以不經(jīng)過內(nèi)部類
就調(diào)用函數(shù),或者用少量代碼就能調(diào)用多種函數(shù),這一點(diǎn)體現(xiàn)了它的“指針”特性。呵
呵,很有“波粒二象性”的味道吧?指代最重要的應(yīng)用在于對于事件的處理,下一節(jié)我
們將重點(diǎn)介紹。
6、事件(Event)
C#對事件是直接支持的(這個(gè)特點(diǎn)也是MSVJ所具有的)。當(dāng)前很多主流程序語言處
理事件的方式各不相同,Delphi采用的是函數(shù)指針(這在Delphi中的術(shù)語是“closure”
)、Java用改編類來實(shí)現(xiàn)、VC用WindowsAPI的消息系統(tǒng),而C#則直接使用delegate和ev
ent關(guān)鍵字來解決這個(gè)問題。下面讓我們來看一個(gè)例子,例子中會(huì)給大家舉出聲明、調(diào)用
和處理事件的全過程。
//首先是指代的聲明,它定義了喚醒某個(gè)函數(shù)的事件信號
public delegate void ScoreChangeEventHandler (int newScore, ref bool cancel)
;
//定義一個(gè)產(chǎn)生事件的類
public class Game
{
// 注意這里使用了event關(guān)鍵字
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)
{
// 裁判負(fù)責(zé)調(diào)整比賽中的分?jǐn)?shù)變化
game.ScoreChange += new ScoreChangeEventHandler (game_ScoreChange);
}
// 注意這里的函數(shù)是怎樣和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!");
}
}
}
// 主函數(shù)類,用于測試上述特性
public class GameTest
{
public static void Main ()
{
Game game = new Game ();
Referee referee = new Referee (game);
game.Score = 70;
game.Score = 110;
}
}
在主函數(shù)中,我們創(chuàng)建了一個(gè)game對象和一個(gè)裁判對象,然后我們通過改變比賽分
數(shù),來觀察裁判對此會(huì)有什么響應(yīng)。
請注意,我們的這個(gè)系統(tǒng)中,Game對象是感覺不到裁判對象的存在的,Game對象在
這里只負(fù)責(zé)產(chǎn)生事件,至于有誰會(huì)來傾聽這個(gè)事件,并為之作出反應(yīng),Game對象是不作
任何表態(tài)的。
我們注意到,在裁判類的Referee函數(shù)中,Game.ScoreChange后面使用了+=和-=操作
符,這是什么意思呢?回到我們定義ScoreChange的地方,可以發(fā)現(xiàn)ScoreChange是用ev
ent關(guān)鍵字修飾的,那么這里的意思就很明白了:ScoreChange是一個(gè)事件,而事件被觸
發(fā)后需要相應(yīng)的事件處理機(jī)制,+=/-=就是為這個(gè)事件增加/移除相對應(yīng)的事件處理程序
,而且,并不是一個(gè)事件只能對應(yīng)一個(gè)處理程序,我們還可以用這兩個(gè)操作符為同一事
件增加/移除數(shù)個(gè)事件處理程序。怎么樣?很方便吧!
在實(shí)際應(yīng)用中,和我們上面講的(競賽-裁判)機(jī)制很相近的系統(tǒng)就是圖形用戶界面
系統(tǒng)了。Game對象可以看作是圖形界面上的小零件,而得分事件就相當(dāng)于用戶輸入事件
,而裁判就相當(dāng)于相應(yīng)的應(yīng)用程序,用于處理用戶輸入。
指代機(jī)制的首次亮相是在MSVJ里,它是由Anders Hejlsberg發(fā)明的,現(xiàn)在又用到了
C#中。指代用在Java語言中的后果,則直接導(dǎo)致了微軟和Sun之間對類和指針的關(guān)系產(chǎn)生
了大量的爭論和探討。有意思的是,Java的發(fā)明者James Gosling非常幽默地稱呼指代的
發(fā)明者Anders Hejlsberg為“‘函數(shù)指針’先生”,因?yàn)锳nders Hejlsberg總是想方設(shè)
法地把指針變相地往各種語言中放;不過有人在看了Java中大量地使用了各種類后,也
戲稱Java的發(fā)明者James Gosling為“‘全都是類’先生”,真是其中滋味,盡在不言中
啊。
本博客為學(xué)習(xí)交流用,凡未注明引用的均為本人作品,轉(zhuǎn)載請注明出處,如有版權(quán)問題請及時(shí)通知。由于博客時(shí)間倉促,錯(cuò)誤之處敬請諒解,有任何意見可給我留言,愿共同學(xué)習(xí)進(jìn)步。