天天看點

從C/C++到C# (1)

    用了10多年的C/C++,因為之前一直在Linux下作項目,對C#少有涉獵。今年終于開始要正式學習C#了,将學習過程中的一些體會和筆記摘錄成文和大家共享,由于個人經驗的原因,本文可能更多地着眼在C#和C/C++不同的地方。本系列的第一篇主要列舉了在基礎類型、語句定義、函數方法等方面的差異。

  • C#中程式的入口點方法名為Main,這個函數必須使用static屬性。
  • C#中所有的方法必須位于class内部。
  • Visual C#中的Windows Form程式的Form都有Code View和Design View兩種視圖。不要去改動*.Designer.cs檔案,對這些檔案的改動會在你使用Design View的時候被Visual Studio自動覆寫。
  • C#中所有的變量在使用前都必須被指派/初始化,這被稱為Definite Assignment Rule。

           int age;

           Console.WriteLine(age); // compile time error

  • C#中,帶小數點的數預設類型為double而不是float。
  • C/C++中,你不能對浮點數使用%,但是在C#中可以。
  • C#中每一個class都有一個ToString方法,你可以自己定義這個方法的實作。
  • C#中,如果一個方法沒有傳回值,必須使用void明确說明,不可省略。
  • C#方法中,你必須在使用一個變量前必須聲明;但是方法可以在Class中的field定義語句之前使用相應的field。
  • C/C++中,你可以在if語句的判定條件中使用int表達式等來隐式轉換bool;但是在C#中,你隻能在判定表達式中使用bool類型表達式。
  • C#中的switch語句必須遵循如下要求:如果你得case條件下包含可執行語句,就必須包含break;隻有不包含任何語句的case分支可以不使用break而fall through到下一個分支;default語句也必須包含break,且不一定是最後一個分支。
  • C#中有for each語句來周遊某個集合。
  • C#使用try{}catch{}finally{}來處理異常;發生異常時,第一個比對的catch塊被執行,其他catch塊被忽略;在catch一個異常後,執行回到catch塊的後一條語句,而不是産生異常的方法。
  • 預設情況下,C#允許計算自然溢出,你可以通過checked{}/checked()和unchecked{}/unchecked()來局部調整這個設定,也可以通過build option來改變程式整體的設定。隻有直接位于checked{}塊内的語句會進行溢出檢查,而塊内語句調用的方法不進行檢查。checked和unchecked隻能用于整數,不能用于浮點。如果exception被本地catch,就先執行catch塊,然後執行finally塊;如果exception要被傳遞到上層,就先執行finally語句,然後再傳遞exception。
  • C#中,class内的變量被稱為field。Class是類型,object是執行個體。C#的class中方法和變量的預設屬性是private。和C++不同的是,public/private/protected關鍵字後沒有冒号,你必須對每一個聲明都使用對應的關鍵字。C#構造函數不能傳回任何值(void也不行),如果你不定義構造函數,編譯器會自動生成一個預設構造函數(無參數);如果你定義了任何構造函數,編譯器就不再生成預設構造函數。
  • C#中,class中的field都會被自動初始化成0/false/null。
  • 從C# 2.0開始,可以将一個class的代碼切分到多個檔案中,這時對每個檔案都必須使用partial關鍵字聲明class。
  • C# class中操作某個特定class執行個體内資料的方法稱為instance method;static方法/變量不需要依附于instance,直接使用class名通路,static方法不能通路任何instance field/method,隻能使用static field/method;如果你使用const關鍵字來聲明field(這個field隻能enum/primitive type/string),則這個field自動是static的。
  • C#中static class隻能包含static成員;對static class生成執行個體是沒有意義的;static class隻能由static的預設構造函數
  • C#中所有的primitive type和struct都是value type,使用中是按值傳遞的,使用的空間在declare的時候就配置設定;所有的class都是reference type,使用中按照引用傳遞,他們的實際空間隻有在new的時候配置設定;在C#中,所有的引用都是強類型的,不能将一個引用變量指向一個不同類型的空間(C/C++中的指針幾乎可以指向任何空間)。
  • C#中,當你向一個方法傳遞參數的時候,對應的參數被初始化成傳入資料的拷貝(不論傳入的是value type還是reference type);如果你在一個參數前加上ref關鍵字,參數就變成了傳入值得alias而不是copy(ref關鍵字在定義和調用方法的時候都要出現),在使用ref參數的時候,使用之前必須指派的規則一樣适用;如果你想要在方法内部初始化參數,并将其傳回給調用者,可以使用out關鍵字,這時參數也是alias而不是copy(這時方法内部必須初始化這個參數)。
  • 你調用方法的時候,其參數和内部變量都在stack中配置設定;當你建立一個class執行個體的時候,其使用的記憶體位于heap;所有的value type都建立在stack,所有的reference type都建立在heap(但是指向reference type空間的變量還是位于stack)。Stack是先進後出的,heap是一個flat的空間,自由配置設定和回收。
  • C#中所有的class都是System.Object的子類,System.Object/object類型的變量可以refer向任何reference type;object類型的變量也可以refer向value type,但是因為所有的reference必須位于heap,是以heap中會配置設定一塊區域拷貝value type的value,然後讓object變量指向heap中的這塊區域。這種自動将stack中的item拷貝到heap的行為稱為boxing。通過cast将object中指向的value type重新指派給某個value type變量(二者的類型必須嚴格比對,即使能隐式轉換也不行)的過程叫unboxing。
  • 如果你想要在C#使用指針,必須在方法内部使用unsafe{}将使用指針的代碼包含起來,或者對方法本身使用unsafe修飾;同時在編譯代碼的時候必須使用/unsafe選項。
  • C#中,enum和struct都是value type;可以指定enum的基礎類型(byte/sbyte/short/ushort/int/uint/long/ulong);struct可以包含field/method/constructor;你不能為struct聲明一個default constructor,因為編譯器總是會替你生成一個(将所有的field設定成0/false/null),如果你自己定義了struct的非default constructor,其中必須明确初始化所有的field(class則不必);在class中,可以在聲明instance field的時候初始化,struct則不允許;class可以有base class,struct不能繼承;為了確定struct類型變量中所有field都被初始化,也要和class類型的變量一樣使用new來調用構造函數;在初始化了某個struct類型變量後,可以将其assign給另一個struct變量。
  • C#中使用int[],int[,]來定義Array變量(不必說明大小),使用new來實際配置設定空間(說明大小,打下可以是動态生成的,同時會自動初始化元素);Array是reference type;可以如下初始化int[] pins = new int[4]{ 9, 3, 7, 2 }/int[] pins={9,3,7,2},初始化數值的個數必須和Array的size嚴格比對;所有對Array的通路是有邊界檢查的;Array具有Length屬性,可以用于确認其中包含了多少元素;foreach總是從Index 0開始隻讀地周遊整個Array;Array具有CopyTo/Copy/Clone方法用于拷貝Array的元素到另一個Array。
  • C#中Collection位于System.Collections,其包含的元素類型是object(這意味着你向其中插入/删除value type的時候總是需要boxing/unboxing)。
  • 如果想要動态操作Array,包括改變大小、插入、删除元素,最好使用ArrayList類(具有Remove/RemoveAt/Add/Insert方法,也具有Count屬性);如果要實作一個FIFO的隊列,可以使用Queue類,包含Enqueue/Dequeue方法;如果要實作一個LIFO的棧,可以使用Stack類,包含Push/Pop方法;如果想實作哈希表,可以使用Hashtable類,使用foreach周遊的時候得到DictionaryEntry類,其包含key/value元素;如果要實作一個排序清單,可以使用SortedList類,其也包含key/value,但總是按照key自動排序的
  • Array永遠是可讀可寫的,而Collection中的所有類可以通過ReadOnly方法變為隻讀。
  • 如果你的方法希望使用可變數量的參數表,可以通過Parameter Array的方式,例如public static int Min(params int[] paramList),這點類似于C/C++中的varargs宏(位于stdarg.h);參數數組隻能是一維的,方法重載的時候不能生成僅有params關鍵字不同的方法(也不能生成有潛在模糊性的重載函數),params array不能使用ref或out修飾,params array必須是最後一個參數,在函數重載的時候,non-params的方法總是優先于params方法。
  • 使用params object[]作為參數就可以向方法傳遞任意數量、任意類型的參數。

(待續:本系列第二篇将主要描述C#中繼承、接口等方面的差異性)

繼續閱讀