【翻译】C#编程语言和JAVA编程语言的比较(下)
原文地址:http://www.25hoursaday.com/CsharpVsJava.html
?
6、集合
?
許多有名的編程語言都會包含一個(gè)集合框架,框架一般由各種用于保存數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)和配套的操作對象的算法構(gòu)成。集合框架的優(yōu)勢是讓開發(fā)者可以不用寫數(shù)據(jù)結(jié)構(gòu)和排序算法,把精力放在真正的業(yè)務(wù)邏輯上。還有就是可以讓不同的項(xiàng)目保持一致性,新的開發(fā)者也少了很多學(xué)習(xí)曲線。
C#集合框架大多位于System.Collections和System.Collections.Generic命名空間。Systems.Collections命名空間包含了表示抽象數(shù)據(jù)類型的接口和抽象類,比如IList, IEnumerable, IDictionary, ICollection, 和 CollectionBase,只要數(shù)據(jù)結(jié)構(gòu)從抽象數(shù)據(jù)類型派生,開發(fā)者無需關(guān)心其內(nèi)部如何實(shí)現(xiàn)。System.Collections命名空間還包含了很多數(shù)據(jù)結(jié)構(gòu)的具體實(shí)現(xiàn),包括ArrayList, Stack, Queue, HashTable 和SortedList。這四種結(jié)構(gòu)都提供了同步包裝,可以在多線程程序中線程安全。System.Collections.Generic命名空間實(shí)現(xiàn)了System.Collections空間中主要數(shù)據(jù)結(jié)構(gòu)的泛型版本,包括泛型的List<T>, Stack<T>,Queue<T>, Dictionary<K,T> 和SortedDictionary<K,T>類。
?
Java集合框架則在java.util包中包含許多類和接口。java.util包也同樣支持泛型,并沒有使用新的命名空間來放置泛型類型。Java集合框架和C#相似,不過可以認(rèn)為是C#集合框架的超集,因?yàn)樗鼘?shí)現(xiàn)了一些其它特性。
注意:后面作者的話我就不翻譯了,因?yàn)樗岬降腏ava中有,而C#中沒有的集合在.NET 3.5和.NET 4.0中都已經(jīng)支持
?
7、goto
?
和Java不同,C#包含的goto可以用來在代碼中進(jìn)行跳轉(zhuǎn),盡管goto被嘲笑,但是有的時(shí)候還是可以使用goto來減少代碼重復(fù)并增加可讀性。goto語句第二個(gè)用處是可以重用異常,因?yàn)楫惓伋鍪菬o法跨越方法邊界的。
注意:在C#中g(shù)oto無法跳轉(zhuǎn)到語句塊
?
C# Code using System; using System.Net.Sockets; class GotoSample{public static void Main(string[] args){int num_tries = 0;retry: try{ num_tries++; Console.WriteLine("Attempting to connect to network. Number of tries =" + num_tries);//Attempt to connect to a network times out //or some some other network connection error that //can be recovered fromthrow new SocketException(); }catch(SocketException){if(num_tries < 5)goto retry; } }/* Main(string[]) */ }//GotoSample8、虛方法
?
面向?qū)ο蟮囊粋€(gè)主要特點(diǎn)就是多態(tài)。多態(tài)可以讓我們和繼承體系中的泛化類型而不是實(shí)際類型打交道。也就是一般在基類中實(shí)現(xiàn)的方法在派生類中重寫,我們即使持有基類類型的引用,但是其指向派生類型,在運(yùn)行時(shí)而不是編譯時(shí)動(dòng)態(tài)綁定的方法叫做虛方法。在Java中所有的方法都是虛方法,而在C#中,必須通過virtual關(guān)鍵字顯式指定方法為虛方法,默認(rèn)不是虛方法。同樣可以用override關(guān)鍵字在子類中重寫虛方法或使用new關(guān)鍵字隱藏基類方法。在Java中可以通過標(biāo)記方法為final關(guān)鍵字讓方法不能被派生類重寫,在C#中可以不標(biāo)記virtual來實(shí)現(xiàn)。主要區(qū)別是,如果派生類也實(shí)現(xiàn)了相同方法,C#的可以通過把引用指向基類調(diào)用到基類的方法,而在Java中如果基類實(shí)現(xiàn)了final方法,派生類不允許再有同名的方法。
?
C# Code using System; public class Parent{public void DoStuff(string str){Console.WriteLine("In Parent.DoStuff: " + str); }}public class Child: Parent{public void DoStuff(int n){Console.WriteLine("In Child.DoStuff: " + n); }public void DoStuff(string str){Console.WriteLine("In Child.DoStuff: " + str); } }public class VirtualTest{public static void Main(string[] args){Child ch = new Child(); ch.DoStuff(100); ch.DoStuff("Test"); ((Parent) ch).DoStuff("Second Test"); }}//VirtualTestOUTPUT: In Child.DoStuff: 100 In Child.DoStuff: Test In Parent.DoStuff: Second Test Java Code class Parent{public void DoStuff(String str){System.out.println("In Parent.DoStuff: " + str); }}class Child extends Parent{public void DoStuff(int n){System.out.println("In Child.DoStuff: " + n); }public void DoStuff(String str){System.out.println("In Child.DoStuff: " + str); } }public class VirtualTest{public static void main(String[] args){Child ch = new Child(); ch.DoStuff(100); ch.DoStuff("Test"); ((Parent) ch).DoStuff("Second Test"); }}//VirtualTestOUTPUT: In Child.DoStuff: 100 In Child.DoStuff: Test In Child.DoStuff: Second TestC#的例子可以通過把基類DoStuff(string) 方法標(biāo)記為virtual子類方法標(biāo)記為override關(guān)鍵字來實(shí)現(xiàn)和Java相同輸出:
# Codeusing System; public class Parent{public virtual void DoStuff(string str){Console.WriteLine("In Parent.DoStuff: " + str); }}public class Child: Parent{public void DoStuff(int n){Console.WriteLine("In Child.DoStuff: " + n); }public override void DoStuff(string str){Console.WriteLine("In Child.DoStuff: " + str); } }public class VirtualTest{public static void Main(string[] args){Child ch = new Child(); ch.DoStuff(100); ch.DoStuff("Test"); ((Parent) ch).DoStuff("Second Test"); }}//VirtualTest如上例子可以修改子類的DoStuff(string)方法為如下以得到之前的結(jié)果:
public new void DoStuff(string str)9、文件IO
?
兩種語言都通過Stream類支持IO操作,如下例子把input.txt的內(nèi)容復(fù)制到output.txt中。
C# Code using System; using System.IO; public class FileIOTest {public static void Main(string[] args){FileStream inputFile = new FileStream("input.txt", FileMode.Open);FileStream outputFile = new FileStream("output.txt", FileMode.Open);StreamReader sr = new StreamReader(inputFile);StreamWriter sw = new StreamWriter(outputFile);String str;while((str = sr.ReadLine())!= null)sw.Write(str);sr.Close();sw.Close();}}//FileIOTest Java Code import java.io.*;public class FileIO{public static void main(String[] args) throws IOException {File inputFile = new File("input.txt");File outputFile = new File("output.txt");FileReader in = new FileReader(inputFile);BufferedReader br = new BufferedReader(in);FileWriter out = new FileWriter(outputFile);BufferedWriter bw = new BufferedWriter(out);String str;while((str = br.readLine())!= null)bw.write(str);br.close();bw.close();}}//FileIOTest10、對象序列化
?
對象持久化或叫序列化是通過諸如文件或網(wǎng)絡(luò)讀寫對象的能力。如果在使用程序的時(shí)候?qū)ο蟮臓顟B(tài)必須保存下來,那么對象持久化就很有用。有的時(shí)候以簡單文本形式保存數(shù)據(jù)不夠,以DBMS保存數(shù)據(jù)又勞師動(dòng)眾了,那么可以使用序列化直接保存,還有的時(shí)候可以使用序列化來傳輸類型。C#中可序列化類型標(biāo)記[Serializable]特性。如果C#的類的一些成員不需要在運(yùn)行時(shí)序列化,可以標(biāo)記[NonSerialized]特性。這些字段通常用于計(jì)算或是臨時(shí)的值,不需要保存下來。C#提供了兩種格式來序列化類,XML或二進(jìn)制格式,前者對于人來說更可讀,后者更高效。當(dāng)然我們也可以通過實(shí)現(xiàn)ISerializable接口實(shí)現(xiàn)自定義的序列化方式。
在Java中,對象序列化需要實(shí)現(xiàn)Serializable接口,而transient關(guān)鍵字用于標(biāo)記不需要序列化的成員。默認(rèn)情況下,Java支持序列化對象到二進(jìn)制格式,但是提供了重寫標(biāo)準(zhǔn)序列化過程的方式。需要重寫默認(rèn)序列化的對象需要實(shí)現(xiàn)如下方法簽名:
private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException;
private void writeObject(java.io.ObjectOutputStream stream) throws IOException
?
由于上面的方法是private的,使用readObject和writeObject來實(shí)現(xiàn)自定義序列化的話沒有要實(shí)現(xiàn)的接口,對于需要公開訪問的方法的類實(shí)現(xiàn)自定義序列化可以使用java.io.Externalizable接口,指定readExternal() 和writeExternal()。
C# Codeusing System; using System.IO; using System.Reflection; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Serialization.Formatters.Soap;[Serializable] class SerializeTest{[NonSerialized]private int x; private int y; public SerializeTest(int a, int b){x = a; y = b; }public override String ToString(){return "{x=" + x + ", y=" + y + "}"; }public static void Main(String[] args){SerializeTest st = new SerializeTest(66, 61); Console.WriteLine("Before Binary Write := " + st);Console.WriteLine("\n Writing SerializeTest object to disk");Stream output = File.Create("serialized.bin");BinaryFormatter bwrite = new BinaryFormatter(); bwrite.Serialize(output, st); output.Close(); Console.WriteLine("\n Reading SerializeTest object from disk\n");Stream input = File.OpenRead("serialized.bin");BinaryFormatter bread = new BinaryFormatter(); SerializeTest fromdisk = (SerializeTest)bread.Deserialize(input); input.Close(); /* x will be 0 because it won't be read from disk since non-serialized */ Console.WriteLine("After Binary Read := " + fromdisk);st = new SerializeTest(19, 99); Console.WriteLine("\n\nBefore SOAP(XML) Serialization := " + st);Console.WriteLine("\n Writing SerializeTest object to disk");output = File.Create("serialized.xml");SoapFormatter swrite = new SoapFormatter(); swrite.Serialize(output, st); output.Close(); Console.WriteLine("\n Reading SerializeTest object from disk\n");input = File.OpenRead("serialized.xml");SoapFormatter sread = new SoapFormatter(); fromdisk = (SerializeTest)sread.Deserialize(input); input.Close(); /* x will be 0 because it won't be read from disk since non-serialized */ Console.WriteLine("After SOAP(XML) Serialization := " + fromdisk);Console.WriteLine("\n\nPrinting XML Representation of Object");XmlDocument doc = new XmlDocument(); doc.Load("serialized.xml"); Console.WriteLine(doc.OuterXml);}}Java Code import java.io.*; class SerializeTest implements Serializable{transient int x; private int y; public SerializeTest(int a, int b){x = a; y = b; }public String toString(){return "{x=" + x + ", y=" + y + "}"; }public static void main(String[] args) throws Exception{SerializeTest st = new SerializeTest(66, 61); System.out.println("Before Write := " + st);System.out.println("\n Writing SerializeTest object to disk");FileOutputStream out = new FileOutputStream("serialized.txt");ObjectOutputStream so = new ObjectOutputStream(out); so.writeObject(st);so.flush();System.out.println("\n Reading SerializeTest object from disk\n");FileInputStream in = new FileInputStream("serialized.txt");ObjectInputStream si = new ObjectInputStream(in); SerializeTest fromdisk = (SerializeTest)si.readObject();/* x will be 0 because it won't be read from disk since transient */ System.out.println("After Read := " + fromdisk);}}輸出結(jié)果:
Before Write := {x=66, y=61}
Writing SerializeTest object to disk
Reading SerializeTest object from disk
After Read := {x=0, y=61}
?
11、文檔生成
?
C#和Java都提供了從源文件提取特殊格式的注釋然后集中到一個(gè)文檔中。這些注釋一般是API規(guī)范,這是一種非常有用的方式來生成類庫文檔。生成的文檔也可以在設(shè)計(jì)者、開發(fā)者和QA之間分發(fā)。Javadoc是一種非常有用的工具用于從源代碼提取API文檔。Javadoc從源代碼注釋中提取內(nèi)容生成HTML文檔。可以生成的描述信息包括包、類、成員級別。可以在類或成員變量的描述中提供對其它類或類成員的引用。
Javadoc允許方法有如下信息:
1)描述方法
2)方法拋出的異常
3)方法接收的參數(shù)
4)方法的返回值
5)關(guān)聯(lián)的方法和成員
6)API是否被棄用
7)方法首次提供的時(shí)間
廢棄信息對于編譯器也有用,如果在編譯的時(shí)候編譯器發(fā)現(xiàn)調(diào)用了廢棄的方法可以給予警告。
Javadoc自動(dòng)提供如下信息:
1)繼承的API
2)派生類列表
3)實(shí)現(xiàn)的類或借口
4)類序列化格式
5)包繼承層次
由于Java生成HTML文檔,可以在Javadoc注釋中使用HTML。如下是一個(gè)例子:
Java Code/** * Calculates the square of a number. * @param num the number to calculate. * @return the square of the number. * @exception NumberTooBigException this occurs if the square of the number * is too big to be stored in an int. */public static int square(int num) throws NumberTooBigException{}C#使用XML作為文檔格式。生成的文檔是XML文件,包含了用于提供的元數(shù)據(jù)以及少量自動(dòng)生成的信息。C#的XML文檔在生成的時(shí)候不會包含有關(guān)繼承API列表、派生類或?qū)崿F(xiàn)接口等元數(shù)據(jù)。
XML格式的主要優(yōu)勢是可以以各種方式來用。可以用XSLT樣式來吧生成ASCII文本、HTML等。也可以作為一些工具的數(shù)據(jù)源來生成特殊的文檔。
如下是C# XML文檔的例子:
C# Code///<summary>Calculates the square of a number.</summary> ///<param name="num">The number to calculate.</param> ///<return>The square of the number. </return> ///<exception>NumberTooBigException - this occurs if the square of the number ///is too big to be stored in an int. </exception>public static int square(int num){}?
12、單個(gè)文件中的多個(gè)類
?
兩種語言都可以在單個(gè)文件中定義多個(gè)類,但是有區(qū)別。在Java中,一個(gè)原文件中只可以有一個(gè)public訪問的類并且類名需要和不帶擴(kuò)展名的源文件名保持一致。C#則對一個(gè)文件有多少個(gè)public類以及文件名是否和類名一致都沒有限制。
?
13、導(dǎo)入類庫
?
在應(yīng)用程序中使用類庫有兩個(gè)步驟,首先需要在源文件中使用using或import關(guān)鍵字來引用空間或包,其次需要告訴編譯器哪里有需要的類庫。對于Java來說指定類庫位置可以使用CLASSPATH環(huán)境變量或使用-classpath編譯器選項(xiàng),對于C#則在編譯的時(shí)候指定/r開關(guān)。
?
14、事件
?
所謂事件驅(qū)動(dòng)編程就是一個(gè)對象可以進(jìn)行注冊使得自己在別的獨(dú)享狀態(tài)修改或發(fā)生某個(gè)事件的時(shí)候被通知。事件驅(qū)動(dòng)編程也被稱作發(fā)布訂閱模型或觀察者設(shè)計(jì)模式,并且在圖形用戶接口GUI編程上特別常見。Java和C#都有自己的機(jī)制實(shí)現(xiàn)事件。典型的發(fā)布訂閱模型是一個(gè)一對多的關(guān)系,也就是一個(gè)發(fā)布者對應(yīng)多個(gè)訂閱者。訂閱者在發(fā)布者這里注冊要調(diào)用的方法,訂閱者通過內(nèi)部集合保存訂閱者對象。如果訂閱者感興趣的狀態(tài)改變,發(fā)布者會調(diào)用一個(gè)方法遍歷訂閱者集合調(diào)用回調(diào)方法。
?
在Java中沒有通用的機(jī)制來實(shí)現(xiàn)事件,而是采用了GUI類中使用的設(shè)計(jì)模式。事件一般是java.util.EventObject類的子類,這個(gè)類具有設(shè)置或獲取事件來源的方法。在Java中訂閱者一般實(shí)現(xiàn)接口,并且以Listener結(jié)尾,比如MouseListener, ActionListener, KeyListener,包含一個(gè)回調(diào)方法用于在事件發(fā)生的時(shí)候被發(fā)布者調(diào)用。發(fā)布者一般包含add和Listerner名字組合而成的方法用于添加注冊的訂閱者,比如addMouseListener, addActionListener, addKeyListener。發(fā)布者還包含用于移除訂閱者的方法。這些結(jié)構(gòu)構(gòu)成了Java程序中的事件驅(qū)動(dòng)模型。
?
C#使用委托來提供發(fā)布訂閱模型的顯式支持,事件一般是System.EventArgs類的子類。發(fā)布者具有protected的方法并以O(shè)n為前綴,比如OnClick, OnClose, OnInit,在某個(gè)事件發(fā)生的時(shí)候調(diào)用這個(gè)方法,這個(gè)方法然后會調(diào)用委托并且傳入EventArgs對象的實(shí)例作為參數(shù)。這個(gè)方法作為protected的話派生類就可以直接調(diào)用,無需注冊委托。訂閱者的方法接收和事件委托相同的返回類型和參數(shù)。事件委托一般接收兩個(gè)參數(shù),一個(gè)Object表示事件的源,一個(gè)EventArgs類表示發(fā)生的事件,并且委托是void返回值。在C#中event用于自動(dòng)指定事件驅(qū)動(dòng)中回調(diào)的訂閱者委托。在編譯的時(shí)候,編譯器會增加+=和-=,等同于Java的注冊和移除訂閱者的方法。
?
如下例子演示了一個(gè)類生成20個(gè)隨機(jī)數(shù),然后在遇到偶數(shù)的時(shí)候觸發(fā)事件。
C# Code using System; class EvenNumberEvent: EventArgs{/* HACK: fields are typically private, but making this internal so it * can be accessed from other classes. In practice should use properties. */ internal int number; public EvenNumberEvent(int number):base(){this.number = number;}}class Publisher{public delegate void EvenNumberSeenHandler(object sender, EventArgs e); public event EvenNumberSeenHandler EvenNumHandler; protected void OnEvenNumberSeen(int num){if(EvenNumHandler!= null)EvenNumHandler(this, new EvenNumberEvent(num));}//generates 20 random numbers between 1 and 20 then causes and //event to occur if the current number is even. public void RunNumbers(){Random r = new Random((int) DateTime.Now.Ticks); for(int i=0; i < 20; i++){ int current = (int) r.Next(20); Console.WriteLine("Current number is:" + current);//check if number is even and if so initiate callback callif((current % 2) == 0)OnEvenNumberSeen(current);}//for} }//Publisherpublic class EventTest{//callback function that will be called when even number is seenpublic static void EventHandler(object sender, EventArgs e){Console.WriteLine("\t\tEven Number Seen:" + ((EvenNumberEvent)e).number);}public static void Main(string[] args){Publisher pub = new Publisher(); //register the callback/subscriber pub.EvenNumHandler += new Publisher.EvenNumberSeenHandler(EventHandler); pub.RunNumbers(); //unregister the callback/subscriber pub.EvenNumHandler -= new Publisher.EvenNumberSeenHandler(EventHandler); }} Java Code import java.util.*;class EvenNumberEvent extends EventObject{public int number; public EvenNumberEvent(Object source, int number){super(source); this.number = number;}}interface EvenNumberSeenListener{void evenNumberSeen(EvenNumberEvent ene); }class Publisher{Vector subscribers = new Vector(); private void OnEvenNumberSeen(int num){for(int i=0, size = subscribers.size(); i < size; i++)((EvenNumberSeenListener)subscribers.get(i)).evenNumberSeen(new EvenNumberEvent(this, num));}public void addEvenNumberEventListener(EvenNumberSeenListener ensl){subscribers.add(ensl); }public void removeEvenNumberEventListener(EvenNumberSeenListener ensl){subscribers.remove(ensl); }//generates 20 random numbers between 1 and 20 then causes and //event to occur if the current number is even. public void RunNumbers(){Random r = new Random(System.currentTimeMillis()); for(int i=0; i < 20; i++){ int current = (int) r.nextInt() % 20; System.out.println("Current number is:" + current);//check if number is even and if so initiate callback callif((current % 2) == 0)OnEvenNumberSeen(current);}//for}}//Publisherpublic class EventTest implements EvenNumberSeenListener{//callback function that will be called when even number is seenpublic void evenNumberSeen(EvenNumberEvent e){System.out.println("\t\tEven Number Seen:" + ((EvenNumberEvent)e).number);}public static void main(String[] args){EventTest et = new EventTest();Publisher pub = new Publisher(); //register the callback/subscriber pub.addEvenNumberEventListener(et); pub.RunNumbers(); //unregister the callback/subscriber pub.removeEvenNumberEventListener(et); }}?
運(yùn)行結(jié)果
?
Current number is:19
Current number is:15
Current number is:1
Current number is:1
Current number is:-9
Current number is:-17
Current number is:-1
Current number is:-18
??????? Even Number Seen:-18
Current number is:0
??????? Even Number Seen:0
Current number is:1
Current number is:-2
??????? Even Number Seen:-2
Current number is:-9
Current number is:-4
??????? Even Number Seen:-4
Current number is:17
Current number is:-7
Current number is:1
Current number is:0
??????? Even Number Seen:0
Current number is:15
Current number is:-10
??????? Even Number Seen:-10
Current number is:-9
?
15、跨語言互操作
?
跨語言互操作是在一個(gè)語言中訪問另一個(gè)語言構(gòu)造的能力。在Java中有很多跨語言互操作的方式。首先JNI機(jī)制允許Java程序調(diào)用C或C++甚至匯編語言寫的本機(jī)方法。本機(jī)方法可以使用JNI來訪問Java的特性,比如調(diào)用Java語言方法,初始化和修改Java類,拋出和捕獲異常,進(jìn)行運(yùn)行時(shí)類型檢查,動(dòng)態(tài)加載Java類。要?jiǎng)?chuàng)建JNI程序可以進(jìn)行如下步驟:
1)創(chuàng)建Java程序,把包含本機(jī)方法的聲明標(biāo)記為native方法
2)寫一個(gè)main方法加載步驟6的類庫,然后使用本機(jī)方法
3)使用javac編譯器編譯包含native方法和main的類
4)使用javah編譯器和-jni開關(guān)來生產(chǎn)頭文件和本地方法
5) 使用你選擇的語言寫本機(jī)方法
6) 把頭文件和本機(jī)源文件編譯到共享類庫中,比如Windows的dll或UNIX的.so
?
Java還可以通過Java IDL來和CORBA的分布式對象交互。CORBA應(yīng)用程序一般由對象請求代理ORB、客戶端和服務(wù)端構(gòu)成。ORB負(fù)責(zé)匹配請求客戶端到服務(wù)端,使用對象引用來定位目標(biāo)對象。ORB檢查對象引用的時(shí)候會檢查目標(biāo)對象是否是遠(yuǎn)程的。如果對象時(shí)本地的ORB進(jìn)行進(jìn)程內(nèi)調(diào)用IPC,否則ORB封送參數(shù)并且把調(diào)用通過網(wǎng)絡(luò)路由到遠(yuǎn)程ORB。遠(yuǎn)程ORB然后在本地調(diào)用方法,通過網(wǎng)絡(luò)把結(jié)果發(fā)送回客戶端。CORBA有語言無關(guān)的接口定義語言IDL,各種語言都可以支持CORBA映射。Java IDL支持從Java對象到CORBA IDL對象的映射,各種ORB提供各種語言的CORBA語言綁定,包括C, C++, Java, Python, Lisp, Perl, 和Scheme。
?
在Java中最無縫方式進(jìn)行跨語言交互的方式是直接把Java編譯成字節(jié)碼。Jython腳本語言是Python編程語言整合到Java平臺的一個(gè)版本。如下例子演示了Jython如何創(chuàng)建一個(gè)Java的隨機(jī)數(shù)類型(java.util.Random)并且和這個(gè)類型的實(shí)例進(jìn)行交互。
?
C:\jython>jython Jython 2.0 on java1.2.1 Type "copyright", "credits" or "license" for more information. >>> from java.util import Random >>> r = Random() >>> r.nextInt() -790940041 >>> for i in range(5): ... print r.nextDouble() ... 0.23347681506123852 0.8526595592189546 0.3647833839988137 0.3384865260567278 0.5514469740469587 >>>?
C#和.NET運(yùn)行時(shí)本來的一個(gè)設(shè)計(jì)目標(biāo)就是無縫的跨語言交互。任何.NET公共語言運(yùn)行時(shí)的語言都可以基于公共類型系統(tǒng)CTS互相交互。公共類型系統(tǒng)定義了類型如何聲明,確保各種語言可以共享類型信息。元數(shù)據(jù)是描述程序集、類型和應(yīng)用程序定義的特性的二進(jìn)制信息,它保存在CLR PE中,或者在程序集加載后保存在內(nèi)存中。當(dāng)前.NET運(yùn)行時(shí)支持的語言包括APL, C#, C++, COBOL, Component Pascal, Eiffel, Haskel#/Mondrian, Java, Mercury, Oberon, Perl, Python, Scheme, Smalltalk, ML, 和Visual Basic。由于一種語言中具有的特性很可能在另外一種語言中沒有,.NET框架提供了CLS描述一組基本的語言特性和定義如何使用這些特性的規(guī)則。CLS規(guī)則是公共類型系統(tǒng)的子集,并且通過定義一組編程語言最常見的特性集合來確保跨語言互操作。C#編譯器是CLS兼容的編譯器,也就是說可以用于編譯符合CLS的代碼。C#編譯器可以檢查CLS規(guī)范并且在代碼使用了不符合CLS功能的時(shí)候給出錯(cuò)誤。要讓C#編譯器檢查CLS規(guī)范可以使用[CLSCompliantAttribute(true)]特性。C#支持的另一種跨語言交互是基于COM的對象,這個(gè)機(jī)制允許開發(fā)者在C#中使用COM,反之亦然。在創(chuàng)建了包裝類后,C#對象可以使用COM對象,包裝類可以當(dāng)做普通的C#對象來使用,.NET運(yùn)行時(shí)會處理復(fù)雜的參數(shù)封送操作。可以使用tlbimp工具來自動(dòng)創(chuàng)建包裝類。對于COM對象使用C#對象,必須創(chuàng)建描述C#對象的類型庫,可以使用tlbexp創(chuàng)建類型庫以COM的形式來描述C#對象。還可以使用regasm工具來注冊程序集。COM對象和C#對象交互的時(shí)候,運(yùn)行時(shí)會負(fù)責(zé)COM和.NET之間數(shù)據(jù)的封送。C#程序還可以使用extern關(guān)鍵字和DllImport特性來使用任何DLL的功能,這么做的優(yōu)勢是不需要針對C#的調(diào)用為方法作特殊處理,并且也不需要有包裝來調(diào)用既有的代碼。
?
?
?
第四部分:C#有但Java沒有的地方
?
?
1、對象清理
?
為了提供完全控制類使用的資源,C#提供了System.IDisposable接口,它包含Dispose()方法可以讓類的使用者在使用類之后釋放必要的資源。管理諸如數(shù)據(jù)庫或文件句柄的類可以從這種模式中收益。Dispose提供了一種確定的方式在類不使用的時(shí)候釋放資源,這和Java或C#的終結(jié)器不同。一般會在Dispose方法的實(shí)現(xiàn)中調(diào)用GC類的SupressFinalize 方法,因?yàn)槲覀円话阃ㄟ^Dispose方法顯式釋放資源而不需要運(yùn)行時(shí)的終結(jié)器。C#還提供了諸如using關(guān)鍵字之類的語法糖通過Dispose方法釋放資源。如果類是Disposable的話,最好讓Dispose()方法是冪等的(也就是可以多次調(diào)用Dispose()),可以在Dispose()方法中設(shè)置一個(gè)標(biāo)志位來檢查是否已經(jīng)Dispose。如下例子演示了類保持打開文件,直到Dispose()方法調(diào)用后來表示文件不需要打開了。
?
C# Code using System; using System.IO; public class MyClass : IDisposable { bool disposed = false; FileStream f; StreamWriter sw; private String name;private int numShowNameCalls = 0; MyClass(string name){f = new FileStream("logfile.txt", FileMode.OpenOrCreate); sw = new StreamWriter(f);this.name = name;Console.WriteLine("Created " + name); }~MyClass(){Dispose(false); }public void Dispose(){if(!disposed){Dispose(true);}}private void Dispose(bool disposing){lock(this){ /* prevents multiple threads from disposing simultaneously */ /* disposing variable is used to indicate if this method was called from a * Dispose() call or during finalization. Since finalization order is not * deterministic, the StreamWriter may be finalized before this object in * which case, calling Close() on it would be inappropriate so we try to * avoid that. */if(disposing){ Console.WriteLine("Finalizing " + name); sw.Close(); /* close file since object is done with */ GC.SuppressFinalize(this);disposed = true; }}}public string ShowName(){if(disposed) throw new ObjectDisposedException("MyClass");numShowNameCalls++; sw.Write("ShowName() Call #" + numShowNameCalls.ToString() + "\n"); return "I am " + name; }public static void Main(string[] args){using (MyClass mc = new MyClass("A MyClass Object")){for(int i = 0; i < 10; i++){Console.WriteLine(mc.ShowName()); } //for}/* runtime calls Dispose on MyClass object once "using" code block is exited, even if exception thrown */ }//Main }如上的模式和C++方式的析構(gòu)器很像,只不過不需要考慮內(nèi)存分配。終結(jié)器這種不精確的特性一致被Java開發(fā)者詬病,有了Dispose后這不再是問題了。
注意:調(diào)用Dispose()方法不等同于要求對象被垃圾回收,只不過由于不需要終結(jié)器之后可以加速被回收。
?
2、委托
?
委托是提供回調(diào)函數(shù)的機(jī)制,委托和C或C++的函數(shù)指針相似。委托的一個(gè)用途就是根據(jù)算法使用的類型傳入操作到泛型算法。另一個(gè)用途就是為事件注冊處理程序。在Java中要使用C#委托中相同的功能,可以創(chuàng)建接口然后指定回調(diào)方法,比如Comparable接口,的缺點(diǎn)是方法只能是實(shí)例方法,其實(shí)一般是當(dāng)做靜態(tài)方法來使用的。
要使用委托,首先聲明和要調(diào)用的回調(diào)方法返回值和相同參數(shù)的委托。然后定義接收以委托作為參數(shù)的方法。完成后,使用符合委托的方法來初始化委托的實(shí)例,然后把委托傳入接收委托作為參數(shù)的方法。委托可以接受靜態(tài)方法和實(shí)例方法,甚至同一時(shí)刻接收兩種,因?yàn)槲惺嵌嗖サ摹H缦卵菔玖藙?chuàng)建和使用實(shí)例委托的例子。
C# Code using System;/* Mammal class hierarchy used to show return type covariance */ public class Mammal {public Mammal(){;}public virtual void Speak(){;} }public class Cat : Mammal{public Cat(){;}public override void Speak(){Console.WriteLine("Meow");} }public class Dog : Mammal{public Dog(){;}public override void Speak(){Console.WriteLine("Woof");} }public class Test {// delegate declaration, similar to a function pointer declarationpublic delegate Mammal CallbackFunction(Dog d); public static Cat BarkAndScareCat(Dog d) {d.Speak(); Cat c = new Cat();c.Speak(); return c; }public static Mammal BarkAndReturnHome(Dog d) {d.Speak(); return d; }public static void Main(string[] args){Dog dog = new Dog(); //create delegate using delegate object (old way)CallbackFunction myCallback = new CallbackFunction(BarkAndReturnHome); myCallback(dog);//create delegate using delegate inference (new way) CallbackFunction myCallback2 = BarkAndScareCat;myCallback2(dog);} }委托可以作為參數(shù)傳入方法,和C或C++的函數(shù)指針有點(diǎn)相似:
C# Code using System;//delegate base public class HasDelegates {// delegate declaration, similar to a function pointer declarationpublic delegate bool CallbackFunction(string a, int b);//method that uses the delegate public bool execCallback(CallbackFunction doCallback, string x, int y){ Console.WriteLine("Executing Callback function...");return doCallback(x, y); }}public class FunctionDelegates {public static readonly HasDelegates.CallbackFunction BarFuncCallback = new HasDelegates.CallbackFunction(FunctionBar); public static bool FunctionBar(string a, int b){ Console.WriteLine("Bar: {0} {1}", b, a);return true;}}public class DelegateTest {public static void Main(string[] args){HasDelegates MyDel = new HasDelegates();// with static delegate, no need to know how to create delegateMyDel.execCallback(FunctionDelegates.BarFuncCallback, "Thirty Three", 33);} } // DelegateTest?
3、值類型(結(jié)構(gòu))
?
在Java和C#中,堆上的東西只能等垃圾回收來收集而在棧上的對象會自動(dòng)被系統(tǒng)回收。一般在棧上分配的內(nèi)存會比在堆上略快。
在Java中,所有的類都在堆上創(chuàng)建而基元類型在棧上創(chuàng)建。如果對象很小并且很常用的話只能在堆上分配的話會造成一定的性能負(fù)擔(dān),C#提供了一種機(jī)制可以讓某種類是基于棧分配的,叫做結(jié)構(gòu),其實(shí)C#內(nèi)建的諸如int的基元類型就是使用結(jié)構(gòu)來分配的。和類不同,值類型一般按值傳遞并且不會被垃圾收集。要使用基于棧的類,可以使用struct來替代class關(guān)鍵字。要?jiǎng)?chuàng)建C#結(jié)構(gòu)可以使用和類一樣的new關(guān)鍵字。如果結(jié)構(gòu)使用默認(rèn)構(gòu)造方法語法創(chuàng)建,那么結(jié)構(gòu)的字段都會使用0初始化。但是,不可以為結(jié)構(gòu)定義默認(rèn)構(gòu)造方法。
?
C# Code using System; struct Point {public int x; public int y; public Point( int x, int y){this.x = x; this.y = y;}public override string ToString(){return String.Format("({0}, {1})", x, y); }public static void Main(string[] args){Point start = new Point(5, 9); Console.WriteLine("Start: " + start);/* The line below wouldn't compile if Point was a class */ Point end = new Point(); Console.WriteLine("End: " + end);}} // Point?
4、運(yùn)行時(shí)類型標(biāo)識(as運(yùn)算符)
?
C#的as運(yùn)算符和C++的dynamic_cast結(jié)構(gòu)一樣。as運(yùn)算符的作用是嘗試把類型轉(zhuǎn)換為某種類型,如果不成功的話返回null。
C# Code MyClass mc = o as MyClass; if(mc != null) //check if cast successful mc.doStuff(); 注意:as不能用于值類型。?
5、屬性
?
屬性可以避免直接訪問類的成員和Java的getters以及setters很像。可以使用屬性來訪問類的字段或成員屬性,但又避免使用方法。可以創(chuàng)建只讀、只寫或讀寫屬性,此外還可以創(chuàng)建屬性讓getter和setter具有不同的訪問性(比如public的getter和private的setter),如下是使用屬性的例子:
C# Code using System; public class User {public User(string name){this.name = name; } private string name; //property with public getter and private setterpublic string Name{get{return name; } private set { name = value; }}private static int minimum_age = 13; //read-write property for class member, minimum_agepublic static int MinimumAge{get{return minimum_age; }set{if(value > 0 && value < 100)minimum_age = value; else Console.WriteLine("{0} is an invalid age, so minimum age remains at {1}", value, minimum_age);}}public static void Main(string[] args){User newuser = new User("Bob Hope"); User.MinimumAge = -5; /* prints error to screen since value invalid */ User.MinimumAge = 18; //newuser.Name = "Kevin Nash"; Causes compiler error since Name property is read-only Console.WriteLine("Minimum Age: " + User.MinimumAge); Console.WriteLine("Name: {0}", newuser.Name);} } // User?
6、多維度數(shù)組
?
如下代碼演示了多維數(shù)組和交錯(cuò)數(shù)組的區(qū)別
C# Code using System;public class ArrayTest {public static void Main(string[] args){int[,] multi = { {0, 1}, {2, 3}, {4, 5}, {6, 7} }; for(int i=0, size = multi.GetLength(0); i < size; i++){for(int j=0, size2 = multi.GetLength(1); j < size2; j++){Console.WriteLine("multi[" + i + "," + j + "] = " + multi[i,j]);}}int[][] jagged = new int[4][];jagged[0] = new int[2]{0, 1};jagged[1] = new int[2]{2, 3};jagged[2] = new int[2]{4, 5};jagged[3] = new int[2]{6, 7};for(int i=0, size = jagged.Length; i < size; i++){for(int j=0, size2 = jagged[1].Length; j < size2; j++){Console.WriteLine("jagged[" + i + "][" + j + "] = " + jagged[i][j]);}}}} // ArrayTest?
7、索引器
?
索引器是重寫類[]運(yùn)算符的語法。如果類包含另外一種對象的話,索引器就很有用。索引器的靈活之處在于支持任何類型,比如整數(shù)或字符串、還可以創(chuàng)建索引器允許多維數(shù)組語法,可以在索引器中混合和匹配不同的類型,最后,索引器可以重載。
C# Code using System; using System.Collections;public class IndexerTest: IEnumerable, IEnumerator {private Hashtable list; public IndexerTest (){index = -1; list = new Hashtable(); }//indexer that indexes by numberpublic object this[int column]{get{return list[column];}set{list[column] = value; }}/* indexer that indexes by name */ public object this[string name]{get{return this[ConvertToInt(name)];}set{this[ConvertToInt(name)] = value; }}/* Convert strings to integer equivalents */private int ConvertToInt(string value){string loVal = value.ToLower(); switch(loVal){case "zero": return 0;case "one": return 1;case "two": return 2;case "three": return 3;case "four": return 4;case "five": return 5; default:return 0; }return 0; }/** * Needed to implement IEnumerable interface. */public IEnumerator GetEnumerator(){ return (IEnumerator) this; }/** * Needed for IEnumerator. */ private int index; /** * Needed for IEnumerator. */ public bool MoveNext(){index++;if(index >= list.Count)return false; elsereturn true; }/** * Needed for IEnumerator. */ public void Reset(){index = -1; }/** * Needed for IEnumerator. */ public object Current{get{return list[index];}}public static void Main(string[] args){IndexerTest it = new IndexerTest(); it[0] = "A"; it[1] = "B";it[2] = "C";it[3] = "D"; it[4] = "E";Console.WriteLine("Integer Indexing: it[0] = " + it[0]); Console.WriteLine("String Indexing: it[\"Three\"] = " + it["Three"]);Console.WriteLine("Printing entire contents of object via enumerating through indexer :");foreach( string str in it){Console.WriteLine(str);}}} // IndexerTest?
8、預(yù)處理指令
?
C#包含預(yù)處理器,相當(dāng)于C/C++預(yù)處理器的有限子集。C#預(yù)處理器沒有#include文件的能力也沒有使用#define進(jìn)行文本替換的能力。主要的能力在于使用#define和#undef標(biāo)識符以及通過#if和#elif以及#else選擇編譯某段代碼的能力。#error和#warning指示器可以在編譯的時(shí)候讓指示器之后的錯(cuò)誤或警告消息顯示出來。#pragma指示器用于處理屏蔽編譯器警告消息。最后,#line指示器可以用于編譯器發(fā)現(xiàn)錯(cuò)誤的時(shí)候指定源文件行號。
C# Code #define DEBUG /* #define must be first token in file */ using System; #pragma warning disable 169 /* Disable 'field never used' warning */class PreprocessorTest{int unused_field; public static void Main(string[] args){#if DEBUGConsole.WriteLine("DEBUG Mode := On");#elseConsole.WriteLine("DEBUG Mode := Off");#endif}}?
9、別名
?
using關(guān)鍵字可以用于為完全限定名設(shè)置別名,和C/C++的typedef相似。如果類的完全限定名需要解決命名空間沖突的話,這就很有用了。
C# Code using Terminal = System.Console; class Test{public static void Main(string[] args){Terminal.WriteLine("Terminal.WriteLine is equivalent to System.Console.Writeline"); }}?
10、運(yùn)行時(shí)代碼生成
?
Reflection.Emit命名空間包含了一些類可以用于生成.NET中間語言,以及在運(yùn)行時(shí)在內(nèi)存中構(gòu)建類甚至把PE文件寫到磁盤上。這類似Java的那些通過生成Java字節(jié)碼寫入磁盤,用于在運(yùn)行時(shí)創(chuàng)建Java類然后被程序使用的類庫。Reflection.Enmit命名空間主要的用戶是編譯器或腳本引擎的作者。比如,System.Text.RegularExpressions使用Reflection.Emit類庫來為每一個(gè)編譯后的表達(dá)式生成自定義匹配引擎。
?
?
11、指針和不安全的代碼
?
盡管C#和Java一樣,不能使用指針類型,但是如果C#代碼在unsafe上下文中執(zhí)行的話就可以使用指針類型。如果C#代碼在unsafe上下文中執(zhí)行,那么就會禁止許多運(yùn)行時(shí)檢查,程序也必須在所運(yùn)行的機(jī)器上有完全信任權(quán)限。寫unsafe代碼的語法和語義和在C和C++中使用指針的語法和語義相似。寫unsafe代碼時(shí),必須使用unsafe關(guān)鍵字把代碼塊指定為unsafe的,并且程序必須使用/unsafe編譯器開關(guān)編譯。由于垃圾回收期可能會在程序執(zhí)行的過程中重新分配托管變量,所以在fixed代碼塊中使用托管變量的時(shí)候需要使用fixed關(guān)鍵字來固定變量地址。如果沒有fixed關(guān)鍵字的話,標(biāo)記和壓縮垃圾回收期可能會在回收的過程中移動(dòng)變量的地址。
?
C# Code using System; class UnsafeTest{public static unsafe void Swap(int* a, int*b){int temp = *a; *a = *b; *b = temp; }public static unsafe void Sort(int* array, int size){for(int i= 0; i < size - 1; i++)for(int j = i + 1; j < size; j++)if(array[i] > array[j])Swap(&array[i], &array[j]); }public static unsafe void Main(string[] args){int[] array = {9, 1, 3, 6, 11, 99, 37, 17, 0, 12}; Console.WriteLine("Unsorted Array:"); foreach(int x in array)Console.Write(x + " "); fixed( int* iptr = array ){ // must use fixed to get address of arraySort(iptr, array.Length);}//fixed Console.WriteLine("\nSorted Array:"); foreach(int x in array)Console.Write(x + " "); }}?
12、按引用傳遞
?
在Java中,傳給方法的參數(shù)是按值傳遞的,方法操作的是復(fù)制的數(shù)據(jù)而不是原來的數(shù)據(jù)。在C#中,可以指定參數(shù)按照引用傳遞而不是數(shù)據(jù)拷貝。有的時(shí)候如果希望方法返回超過一個(gè)對象的時(shí)候就有用。指定參數(shù)按引用傳遞的關(guān)鍵字是ref和out。區(qū)別是使用ref的話傳入?yún)?shù)必須初始化,而out則不需要。
Java Code class PassByRefTest{public static void changeMe(String s){s = "Changed"; }public static void swap(int x, int y){int z = x;x = y;y = z;}public static void main(String[] args){int a = 5, b = 10; String s = "Unchanged"; swap(a, b); changeMe(s); System.out.println("a := " + a + ", b := " + b + ", s = " + s);} }OUTPUT a := 5, b := 10, s = UnchangedC# Codeusing System; class PassByRefTest{public static void ChangeMe(out string s){s = "Changed"; }public static void Swap(ref int x, ref int y){int z = x;x = y;y = z;}public static void Main(string[] args){int a = 5, b = 10; string s; Swap(ref a, ref b); ChangeMe(out s); Console.WriteLine("a := " + a + ", b := " + b + ", s = " + s);} }OUTPUT a := 10, b := 5, s = Changed?
13、逐字字符串
?
C#提供了一種方式來避免在字符串常量中使用轉(zhuǎn)移序列。唯一的例外是雙引號需要使用兩個(gè)雙引號,可以在字符串聲明的時(shí)候使用@來聲明逐字字符串。
C# Code using System; class VerbatimTest{public static void Main(){//verbatim string string filename = @"C:\My Documents\My Files\File.html"; Console.WriteLine("Filename 1: " + filename);//regular string string filename2 = "C:\\My Documents\\My Files\\File.html"; Console.WriteLine("Filename 2: " + filename2);string snl_celebrity_jeopardy_skit = @"Darrell Hammond (Sean Connery) : I'll take ""Swords"" for $400Will Farrell (Alex Trebek) : That's S-Words, Mr Connery.";Console.WriteLine(snl_celebrity_jeopardy_skit);}}?
14、溢出檢查
?
C#提供了顯式檢測或忽略溢出條件的選項(xiàng)。溢出條件檢測到之后會拋出System.OverflowException。由于溢出檢查會帶來性能損失,所以需要通過/checked+編譯選項(xiàng)顯式啟用。可以通過在代碼塊聲明checked來表示代碼總是進(jìn)行溢出檢查,或是使用unchecked表示總是取消溢出檢查。
C# Code using System; class CheckedTest{public static void Main(){int num = 5000; /* OVERFLOW I */byte a = (byte) num; /* overflow detected only if /checked compiler option on *//* OVERFLOW II */checked{byte b = (byte) num; /* overflow ALWAYS detected */ }/* OVERFLOW III */unchecked{byte c = (byte) num; /* overflow NEVER detected */ }}//Main }?
15、顯式接口實(shí)現(xiàn)
?
有的時(shí)候在實(shí)現(xiàn)接口的時(shí)候可能會遇到?jīng)_突,比如FileRepresentation類可能會實(shí)現(xiàn)IWindow和IFileHandler接口,他媽兩個(gè)接口都有Close方法,IWindow的Close方法表示關(guān)閉GUI窗口,而IFileHandler 的Close方法表示關(guān)閉文件。在Java中除了只寫一個(gè)Close方法之外別無他法,而在C#中可以為每一個(gè)接口寫一個(gè)實(shí)現(xiàn)。比如對于之前提到的例子,FileRepresentation類可以有兩個(gè)不同的Close方法。注意,顯式接口方法是private的,并且只有轉(zhuǎn)換成相應(yīng)類型之后才能進(jìn)行方法調(diào)用。
C# Code using System;interface IVehicle{//identify vehicle by model, make, yearvoid IdentifySelf(); }interface IRobot{//identify robot by namevoid IdentifySelf();}class TransformingRobot : IRobot, IVehicle{string model; string make;short year; string name;TransformingRobot(String name, String model, String make, short year){this.name = name;this.model = model; this.make = make; this.year = year; }void IRobot.IdentifySelf(){Console.WriteLine("My name is " + this.name);}void IVehicle.IdentifySelf(){Console.WriteLine("Model:" + this.model + " Make:" + this.make + " Year:" + this.year);}public static void Main(){TransformingRobot tr = new TransformingRobot("SedanBot", "Toyota", "Corolla", 2001); // tr.IdentifySelf(); ERROR IVehicle v = (IVehicle) tr; IRobot r = (IRobot) tr; v.IdentifySelf(); r.IdentifySelf(); } }OUTPUT Model:Toyota Make:Corolla Year:2001 My name is SedanBot?
16、友元程序集
?
友元程序集特性允許內(nèi)部類型或內(nèi)部方法被其它程序集訪問。可以使用[InternalsVisibleToAttribute]特性來實(shí)現(xiàn)友元程序集。如下代碼演示了2個(gè)源文件編譯成2個(gè)程序集來使用友元程序集特性。
C# Code // friend_assembly_test.cs // compile with: /target:library using System.Runtime.CompilerServices; using System;[assembly:InternalsVisibleTo("friend_assembly_test_2")]// internal by default class Friend {public void Hello() {Console.WriteLine("Hello World!");} }// public type with internal member public class Friend2 {internal string secret = "I like jelly doughnuts"; }C# Code 2 // friend_assembliy_test_2.cs // compile with: /reference:friend_assembly_test.dll /out:friend_assembly_test_2.exe using System;public class FriendFinder {static void Main() {// access an internal typeFriend f = new Friend();f.Hello();Friend2 f2 = new Friend2();// access an internal member of a public typeConsole.WriteLine(f2.secret);} }?
17、命名空間限定符
?
項(xiàng)目越大越有可能發(fā)生命名空間沖突。C#有::運(yùn)算符來指定命名空間的作用域。運(yùn)算符左邊的操作數(shù)表示的是解決沖突的作用域,右邊的操作數(shù)表示的是要解決沖突的名字。左操作數(shù)可以是關(guān)鍵字global指代全局作用域或是命名空間的別名。
C# Code using System; using sys = System; namespace TestLib{class Test{public class System {} static DateTime Console = DateTime.Now; public static void Main(){//Console.WriteLine("Hello world"); doesn't work due to static member variable named 'Console'//System.Console.WriteLine("Hello world"); doesn't work due to nested class named 'System'global::System.Console.WriteLine("The time is " + Console); sys::Console.WriteLine("Hello again"); }}?
18、迭代器
?
?
對于支持foreach循環(huán)的數(shù)據(jù)結(jié)構(gòu),必須實(shí)現(xiàn)或返回System.Collections.IEnumerable的實(shí)例。枚舉器寫起來還是有點(diǎn)麻煩的,yield關(guān)鍵字可以把任何方法或?qū)傩赞D(zhuǎn)換為枚舉器。可以使用yield return語句來一個(gè)一個(gè)返回內(nèi)容,可以使用yield break來表示結(jié)束了序列。方法或?qū)傩员仨毞祷豂Enumerable, IEnumerable<T>, IEnumerator 或IEnumerator<T>中的一個(gè)。
C# Code using System; using System.Collections;class Test{public static string[] fruit = {"banana", "apple", "orange", "pear", "grape"};public static string[] vegetables = {"lettuce", "cucumber", "peas", "carrots"};public static IEnumerable FruitAndVeg{get{foreach(string f in fruit){yield return f; }foreach(string v in vegetables){yield return v; }yield break; //optional}}public static void Main(){foreach (string produce in Test.FruitAndVeg){Console.WriteLine(produce);}}}?
19、部分類
?
部分類特性使得我們可以在多個(gè)源文件中定義單個(gè)類、接口或接口。對于自動(dòng)生成的代碼特別有用。在這個(gè)特性出現(xiàn)之前,我們可能會修改自動(dòng)生成的代碼,因?yàn)槭謱懘a和自動(dòng)生成的代碼位于一個(gè)文件中。而有了這個(gè)特性,就減少了出現(xiàn)這種情況的可能。可以通過在類聲明中使用partial關(guān)鍵字來啟動(dòng)部分類,如下代碼演示了定義在兩個(gè)源文件中的部分類。注意,一個(gè)源文件中的方法和屬性可以引用另一個(gè)源文件中定義的方法和屬性。
C# Code using System;partial class Test{public static string[] fruit = {"banana", "apple", "orange", "pear", "grape"};public static string[] vegetables = {"lettuce", "cucumber", "peas", "carrots"};public static void Main(){foreach (string produce in Test.FruitAndVeg){Console.WriteLine(produce);}}} C# Code 2 using System; using System.Collections; partial class Test{public static IEnumerable FruitAndVeg{get{foreach(string f in fruit){yield return f; }foreach(string v in vegetables){yield return v; }}}需要注意的是類上的特性會進(jìn)行合并,因此矛盾的特性是不允許的,比如一個(gè)文件中類聲明的是private另一個(gè)文件聲明的是public的。
?
20、可空類型
?
可空類型System.Nullable類型的實(shí)例。可空類型可以表示底層值類型的值也可以表示空值。例如,Nullable<bool>可以表示值true、false和null。可空類型的變量可以使用值的類型加上?運(yùn)算符來聲明。bool?等價(jià)于Nullable<bool>。每一個(gè)可空類型都有HasValue屬性來表示是否具有有效的值或是null。可空類型真實(shí)的值保存在其Value屬性中。GetValueOrDefault()方法可以返回可空類型的值或如果是null的話返回底層值類型的默認(rèn)值。
可空類型在用于把C#對象映射到關(guān)系型數(shù)據(jù)庫的時(shí)候很有用,因?yàn)樵赟QL數(shù)據(jù)庫中可以存在null的有效值。
C# Code using System;public class Test{public static void Main(string[] args){int? x = 5; if(x.HasValue){Console.WriteLine("The value of x is " + x.Value);}x = null; //prints 0 Console.WriteLine(x.GetValueOrDefault());}}??運(yùn)算符叫做空結(jié)合運(yùn)算符,用于測試可空類型的值,并且在值是空的情況下返回另外一個(gè)值。因?yàn)閤??y等價(jià)于x==(nulll ? y : x)。
C# Code using System;public class Test{public static void Main(string[] args){int? x = null; int y = x ?? 5;//prints 5 Console.WriteLine(y);}}?
?
21、匿名方法
?
匿名方法是和委托相關(guān)的一個(gè)特性。匿名方法是用于以匿名形式聲明委托的方法,而不需要一個(gè)獨(dú)立的方法。如下代碼比較了使用匿名方法和具名方法的委托:
C# Code using System;public class Test {// delegate declaration, similar to a function pointer declarationpublic delegate void CallbackFunction(string a, int b);public static void PrintString(string a, int b){for(int i = 0; i < b ; i++)Console.WriteLine("{0}.) {1}", i + 1, a);}public static void Main(string[] args){/* anonymous code block */CallbackFunction cf = delegate (string a, int b){ for(int i = 0; i < b ; i++)Console.WriteLine("{0}.) {1}", i + 1, a);};cf("Thirty Three", 10); /* using a named delegate function */CallbackFunction cf2 = new CallbackFunction(Test.PrintString); cf2("Twenty Two", 5); } }在使用的時(shí)候要記住匿名方法有一些限制,比如諸如break、goto和contiune之類的跳轉(zhuǎn)語句不能用于從匿名方法跳轉(zhuǎn)到外部。匿名方法也不能引用定義在外部方法的ref或out參數(shù)。
?
?
(其實(shí)還有很多,感嘆C#的偉大)
?
?
?
第五部分:Java有但C#沒有的地方
?
1、受檢查的異常
?
在異常這個(gè)概念出現(xiàn)之前,大多數(shù)異常處理都是通過返回代碼進(jìn)行。異常對于返回值來說有很多優(yōu)勢:
1)提供了一致的模型來處理錯(cuò)誤和其它非預(yù)期的情況
2)如果沒有在當(dāng)前上下文中處理異常的話可以向上傳播
3)開發(fā)者可以把處理錯(cuò)誤的代碼和普通業(yè)務(wù)邏輯分離
Java創(chuàng)建了額外的機(jī)制來處理受檢查的異常和不受檢查的異常。對于受檢查的異常,調(diào)用的方法必須捕獲異常,或通過throws聲明異常必須被其調(diào)用方法處理。從另外一方面來說,不受檢查的異常不需要catch也或用throws子句聲明,不受檢查的異常和返回代碼一樣不會讓編譯器出警告或錯(cuò)誤,但是如果在運(yùn)行時(shí)忽略異常的話同樣會終止程序。受檢查的異常一般用于告訴調(diào)用者如何和為什么會發(fā)生調(diào)用失敗。不受檢查的異常是一般程序中大部分地方都會發(fā)生的異常,如果都要進(jìn)行顯式檢查的話開銷比價(jià)值大。比如空對象引用的異常或是數(shù)組越界的異常,如果是收檢查的異常話,就需要在每一個(gè)訪問對象或訪問數(shù)組的時(shí)候都進(jìn)行try catch,因此比較適合不受檢查的異常。
?
在C#中所有異常都是未收檢查的異常,也沒有throws子句。這么做的主要劣勢是API只能通過文檔來告訴調(diào)用者自己會拋出哪些異常。比如對于如下的代碼,唯一知道下面方法會出現(xiàn)哪些異常的方法是查看所有調(diào)用方法的源代碼或這些方法的源代碼。
?
public string GetMessageFromServer(string server) {//Set up variables and String to write to the serverEncoding ASCII = Encoding.ASCII;string Get = "GET / HTTP/1.1\r\nHost: " + server + "\r\nConnection: Close\r\n\r\n";Byte[] ByteGet = ASCII.GetBytes(Get);Byte[] RecvBytes = new Byte[256];String strRetPage = null;// IPAddress and IPEndPoint represent the endpoint that will// receive the request// Get first IPAddress in list return by DNSIPAddress hostadd = Dns.Resolve(server).AddressList[0];IPEndPoint EPhost = new IPEndPoint(hostadd, 80);//Create the Socket for sending data over TCPSocket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp );// Connect to host using IPEndPoints.Connect(EPhost);if (!s.Connected){strRetPage = "Unable to connect to host";return strRetPage;}// Sent the GET text to the hosts.Send(ByteGet, ByteGet.Length, 0);// Receive the page, loop until all bytes are receivedInt32 bytes = s.Receive(RecvBytes, RecvBytes.Length, 0);strRetPage = "Default HTML page on " + server + ":\r\n";strRetPage = strRetPage + ASCII.GetString(RecvBytes, 0, bytes);while (bytes > 0){bytes = s.Receive(RecvBytes, RecvBytes.Length, 0);strRetPage = strRetPage + ASCII.GetString(RecvBytes, 0, bytes);}return strRetPage;}上面的代碼取自.NET框架Beta2文檔的Socket類。注意到,在這段代碼中沒有捕獲異常。如下是根據(jù)文檔得出的這個(gè)方法可能拋出的異常:
(后面關(guān)于有無這個(gè)特性好壞的爭論就不翻譯了)
?
2、跨平臺移植
?
Java技術(shù)一個(gè)很大的賣點(diǎn)就是Java寫的應(yīng)用程序一次編寫到處運(yùn)行。Sun官方支持Linux、Solaris 和 Windows,其它一些公司也實(shí)現(xiàn)了OS/2, AIX 和MacOS平臺的Java。.NET也通過Mono項(xiàng)目和Ximian提供了移植性的支持。
?
3、擴(kuò)展
?
Java擴(kuò)展機(jī)制允許開發(fā)者擴(kuò)展核心Java平臺。開發(fā)者可以創(chuàng)建讓Java運(yùn)行時(shí)當(dāng)做認(rèn)為是核心Java類的類和包,比如java.lang, java.util, java.net等。
?
4、strictfp
?
在Java中strictfp是可以用于類、方法或借口聲明的修飾符,用于確保符合IEEE 754的精確浮點(diǎn)數(shù)算術(shù)。當(dāng)對一個(gè)類或接口使用 strictfp 關(guān)鍵字時(shí),該類中的所有代碼,包括嵌套類型中的初始設(shè)定值和代碼,都將嚴(yán)格地進(jìn)行計(jì)算。嚴(yán)格約束意味著所有表達(dá)式的結(jié)果都必須是 IEEE 754 算法對操作數(shù)預(yù)期的結(jié)果,以單精度和雙精度格式表示。
?
Java Codepublic class FPTest {static strictfp double halfOfSquareFP(double n){return n * 4.0 * 0.5;}static double halfOfSquareNFP(double n){return n * 4.0 * 0.5;}public static void main(String[] args) {double d = 6.6e+307;System.out.println(halfOfSquareFP(d));System.out.println(halfOfSquareNFP(d)); }}//FPTest?
5、動(dòng)態(tài)類加載
?
Java中在運(yùn)行時(shí)動(dòng)態(tài)加載類的能力非常強(qiáng)大,動(dòng)態(tài)類加載使得Java應(yīng)用程序可以下載目標(biāo)機(jī)器上沒有的class文件。在一個(gè)機(jī)器上的對象類型可以無縫傳輸?shù)狡渌鼨C(jī)器。新的類型可以引入遠(yuǎn)程機(jī)器,可以在運(yùn)行時(shí)擴(kuò)展遠(yuǎn)程應(yīng)用程序的行為。如下例子演示了遠(yuǎn)程應(yīng)用程序接收類型實(shí)現(xiàn)某個(gè)接口:
Java Codepublic class MyRMIServer extends UnicastRemoteObjectimplements SomeInterface {public MyRMIServer() throws RemoteException{ super();}public String obtainName(IStockTicker ticker){String stock_ticker = ticker.getTicker(); if(stock_ticker.equalsIgnoreCase("MSFT"))return "Microsoft Corporation"; else if(stock_ticker.equalsIgnoreCase("SUNW")) return "Sun Microsystems"; elsereturn "Unknown Stock Ticker"; }/* obtainName(IStockTicker) */}obtainName() 遠(yuǎn)程方法接收實(shí)現(xiàn)IStockTicker接口的類型,遠(yuǎn)程客戶端可以調(diào)用這個(gè)方法然后傳入實(shí)現(xiàn)IStockTicker的類型,例如NASDAQStock,如果MyRMIServer 所謂遠(yuǎn)程機(jī)器沒有類的話,整個(gè)NASDAQStock 需要的類都會自動(dòng)傳輸?shù)竭h(yuǎn)程機(jī)器。
?
6、包含字段的接口
?
在Java中,接口中可以聲明常量,在實(shí)現(xiàn)的類中可以使用這個(gè)常量,這在C#中是沒有的。這其實(shí)無關(guān)緊要,因?yàn)樵纫@么這么用的主要原因是模擬枚舉。
?
7、匿名內(nèi)部類
?
匿名內(nèi)部類是類的聲明位于類創(chuàng)建實(shí)例內(nèi)的類。匿名內(nèi)部類一般用于在應(yīng)用程序中只會有一個(gè)類的實(shí)例,最常用的就是在GUI類庫中指定回調(diào)。如下是使用匿名內(nèi)部類來實(shí)現(xiàn)狀態(tài)設(shè)計(jì)模式的例子:
Java Code/* An instance of this class represents the current state of a ClientView GUI. */public abstract class ClientState{// This instance of the class is used to signify that the user is not logged in.// The only thing a user can do in this state is login and exit. public static ClientState NOT_LOGGED_IN = new ClientState() {public void setMenuState(ClientView cv) {cv.setMenuEnabledState(false); /* disable all menus */cv.menuLogin.setEnabled(true); cv.menuExit.setEnabled(true); //can't type cv.textArea.setEnabled(false); }public String toString(){return "ClientState: NOT_LOGGED_IN"; }};// This instance of the class is used to signify that the user is logged in// but has not yet created a document to work with. The user cannot type or save // anything in this mode. public static ClientState NO_OPEN_DOCUMENT = new ClientState() {public void setMenuState(ClientView cv) {cv.setMenuEnabledState(false); /* disable all menus */cv.menuLogin.setEnabled(true); cv.menuExit.setEnabled(true); cv.menuOpenFile.setEnabled(true);cv.menuNewFile.setEnabled(true);//can't type cv.textArea.setEnabled(false); }public String toString(){return "ClientState: NO_OPEN_DOCUMENT"; }}; // This instance of the class is used to signify that the user is editting a file. // In this mode the user can use any functionality he/she sees fit. public static ClientState EDITTING_DOCUMENT = new ClientState() {public void setMenuState(ClientView cv) {cv.setMenuEnabledState(true); /* enable all menus cv.textArea.setEnabled(true); }public String toString(){return "ClientState:EDITTING_DOCUMENT"; }}; // Default constructor private to stop people from directly creating instances // of the class. private ClientState() {;}// This disables various elements of the ClientView's menu dependent on which// ClientState object this is. public abstract void setMenuState(ClientView cv);} // ClientState如下是使用ClientState類的例子
bool loginUser(String username, String passwd) {//check if already logged inif(myGUI.state == ClientState.NOT_LOGGED_IN) return true; //enable parts of the GUI if the user authenticatesif(userAuthenticated(username, passwd)){myGUI.state = ClientState.NO_OPEN_DOCUMENT; myGUI.state.setMenuState(myView); return true; }return false; }/* loginUser(String, String) */?
8、靜態(tài)導(dǎo)入
?
靜態(tài)導(dǎo)入特性使我們可以訪問類靜態(tài)成員的時(shí)候不需要指定類名,這個(gè)特性可以讓我們減少代碼的冗余,特別對于某些幫助類型來說很方便。靜態(tài)導(dǎo)入和普通的import語句很像,只不過多了static關(guān)鍵字,導(dǎo)入的是類而不是包名。
Java Code import static java.awt.Color.*; public class Test{public static void main(String[] args) throws Exception{//constants not qualified thanks to static importSystem.out.println(RED + " plus " + YELLOW + " is " + ORANGE);} }輸出:
java.awt.Color[r=255,g=0,b=0] plus java.awt.Color[r=255,g=255,b=0] is java.awt.Color[r=255,g=200,b=0]
?
?
參考
總結(jié)
以上是生活随笔為你收集整理的【翻译】C#编程语言和JAVA编程语言的比较(下)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一维有限元法matlab,有限元matl
- 下一篇: c# char unsigned_dll