1. 程式人生 > >C#高階程式設計之C#語言

C#高階程式設計之C#語言

1 .NET體系結構

        1.1 C#與.NET的關係

            C#是一種相當新的程式語言,C#的重要性體現在以下兩個方面:

                它是專門為與Microsoft的.NET Framework一起使用而設計的(.NET Framework是一個功能非常豐富的平臺,可開發、部署和執行分散式應用程式)。

                它是一種基於現代面向物件設計方法的語言,在設計它時,Micosoft還吸取了其他所有類似語言的經驗。

            一個很重要的問題要弄明白:C#就其本身而言只是一種語言,儘管它是用於生成面向.NET環境的程式碼,但它本身不是.NET的一部分。.NET支援的一些特性,C#並不支援。而C#語言支援的另一些特性,,NET卻不支援(如運算子過載)。

        1.2 公共語言執行庫

            .NET Framework的核心是其執行庫執行環境,稱為公共語言執行庫(CLR)或.NET執行庫。通常將在CLR控制下執行的程式碼稱為託管程式碼(managed code)。

            在CLR執行編寫好的原始碼(在C#中或其它語言中編寫的程式碼)之前,需要編譯他們。在.NET中,編譯分為兩個階段:

                1)把原始碼編譯為Microsoft中間語言(IL)。

                2)CLR把IL編譯為平臺專用的程式碼。

        1.3 中間語言

            語言互操作性:

                1)用一種語言編寫的類應能繼承用另一種語言編寫的類。

                2)一個類應能包含另一個類的例項,而不管兩個類是使用什麼語言編寫的。

                3)一個物件應能直接呼叫用其他語言編寫的另一個物件的方法。

                4)物件(或物件的引用)應能在方法之間傳遞。

                5)在不用的語言之間呼叫方法時,應能在偵錯程式中交替除錯

            垃圾回收的一個重要方面是它的不確定性。換言之,不能保證什麼時候會呼叫垃圾回收器:CLR決定需要它時,就可以呼叫它。但可以重寫這個過程,在程式碼中呼叫垃圾回收器。

        1.4 程式集

            程式集(assembly)是包含編譯好的,面向.NET Framework的程式碼的邏輯單元。

            注意:可執行程式碼和庫程式碼使用相同的程式集結構,唯一的區別是可執行的程式集包含一個主程式的入口點,而庫程式集不包含。

            共享程式集是其他應用可以使用的公共庫。因為其他軟體可以訪問共享程式集,所以需要採取一定的保護措施來防止以下風險:

                1)名稱衝突,另一個公司的共享程式集執行的型別與自己的共享程式集中的型別同名。因為客戶端程式碼理論上可以同時訪問這些程式集,所以這是一個很嚴重的問題。

                2)程式集被同一個程式集的不同版本覆蓋--新版本與某些已有的客戶端程式碼不相容。

        1.5 .NET Framework類

        1.6 名稱空間

            如果沒有顯式提供名稱空間,型別就新增到一個沒有名稱的全域性名稱空間中。

        1.7 用C#建立.NET應用程式

            1)建立ASP.NET應用程式。

            2)建立Windows窗體。

            3)使用MPF。

            4)Windows控制元件。

            5)Windows服務。

            6)WCF。

            7)Windows WF。

        1.8 C#在.NET企業體系結構中的作用

2. 核心C#

        2.2 變數

            1)變數的初始化

                C#編譯器需要用某個初始值對變數進行初始化,之後才能在操作中引用該變數。大多數現代編譯器把沒有初始化標記為警告,單C#編譯器把它當作錯誤來看待。

                C#有兩個方法可確保變數在使用前進行了初始化:

                    變數是類或結構中的欄位,如果沒有顯式初始化,建立這些變數時,其預設值就是0。

                    方法的區域性變數必須在程式碼中顯示初始化,之後才能在語句中使用它們的值。

            2)型別推斷

                型別推斷使用var關鍵字。宣告變數的語法有些變化。編譯器可以根據變數的初始化值”推斷“變數的型別。

                需要遵循一些規則:

                    變數必須初始化。否則,編譯器就沒有推斷變數型別的依據。

                    初始化不能為空。

                    初始化器必須放在表示式中。

                    不能把初始化設定為一個物件,除非在初始化器中建立了一個新物件。

                注:聲明瞭變數,推斷出了型別後,就不能改變變數型別了。變數的型別確定後,就遵循其它變數型別遵循的強化型別化規則。

            3)常量

                常量總是靜態的。但注意,不必(實際上是不允許)在常量宣告中包含修飾符static。

        2.3 預定義資料型別

            C#把資料型別分為兩種:值型別和引用型別。值型別儲存在堆疊中,而引用型別儲存在託管堆上。特殊的型別是,結構體是值型別,字串是引用型別。

            如果對一個整數是int、uint、long或ulong沒有任何顯式的生命,則該變數預設為int型別。為了把輸出的值指定為其它整數型別,可以在數字後面加上字尾。

            如果在程式碼中沒有對某個非整數值硬編碼,則編譯器一般假定改變數是double。如果想指定該值為float,可以在其後加上字元F(或f)。

            decimal型別標識精度更高的浮點數,但應注意,decimal型別不是基本型別,所以在計算時使用該型別會有效能損失。字尾是M(或m)。

            bool值和整數值不能相互隱式轉換。只能使用值true和false。如果試圖使用0表示false,非0值表示true,就會出錯。

            char為了儲存單個字元的值。

            object型別就是最終的父型別,所有內建型別和使用者定義的型別都從它派生而來。這樣,object型別就可以用於兩個目的:

                可以使用object引用繫結任何子型別的物件。

                object型別執行許多一般用途的基本方法,包括Equals、GetHashCode、GetType和ToString。

            string是一個引用型別,當把一個字元變數賦予另一個字串時,會得到對記憶體中同一個字串的兩個引用,但是字串是不可改變的。修改其中一個字串,就會建立一個全新的string物件,而另一個字串不發生任何變化。

            可以再字串變數的前面加上字元@,在這個字元後的所有字元都看作是其原來的含義--它們不會解釋為轉義字元。

        2.4 流控制

            C#中的switch語句與C++中的switch語句的另一個不同之處:在C#中,可以把字串用作測試的變數。

            foreach迴圈不能改變集合中各項的值,如果需要迭代集合中的各項,並改變它們的值,就應使用for迴圈。

        2.5 列舉

            列舉是使用者定義的整數型別。列舉的真正強大之處是它們在後臺會例項化為派生於基類System.Enum的結構。這表示可以對它們呼叫方法,執行有用的任務。

            在語法上把列舉當作結構是不會造成效能損失。實際上,一旦程式碼編譯好,列舉就成為基本型別,與int和float類似。

            可以獲取列舉的字串表示,還可以從字串中獲取列舉值。

        2.6 名稱空間

            名稱空間提供了一種組織相關類和其它型別的方式。名稱空間是一種邏輯組合,而不是物理組合。

            名稱空間可以引用其它名稱空間,用using關鍵字;名稱空間內也可以巢狀定義名稱空間。

            注意:不允許在另一個巢狀的名稱空間中宣告多部分的名稱空間。

            名稱空間與程式集無關。同一個程式集中可以有不同的名稱空間,也可以在不同的程式集中定義同一名稱空間中的型別。

            如果using語句引用的兩個名稱空間包含同名的型別,就必須使用完整的名稱(或者至少較長的名稱),確保編譯器知道訪問哪個型別。

            using語句與C++中的#include語句相混淆是錯誤,using語句在這些檔案之間並沒有建立物理連結。C#也沒有對應於C++標頭檔案的部分。

            using關鍵字的另一個用途是給類和名稱空間指定別名。

        2.10 註釋

            根據特定的註釋自動建立XML格式的文件說明。這些註釋都是單行註釋,單都以斜槓(///)開頭,而不是通常的兩條斜槓。在這些註釋中,可以把包含型別和型別成員的文件說明的XML標記放在程式碼中。

        2.11 C#預處理指令

            C#並沒有像C++那樣獨立的前處理器,所謂的預處理指令實際上是由編譯器處理的。儘管如此,C#扔保留了一些前處理器指令名稱,因為這些命令會讓人覺得就是前處理器。

        2.12 C#程式設計規範

            變數名稱定義:可以包含數字字元,但是它們必須以字母或下劃線開頭。並且不能把C#關鍵字用作識別符號,如果必須使用識別符號作為名稱,需要在前面加@字首。識別符號也可以包含Unicode字元。

            命名變數時不使用任何字首。

3 物件和型別

        3.1 類和結構

            類和結構實際上都是建立物件的模板,每個物件都包含資料,並提供了處理和訪問資料的方法。

            結構和類的區別是它們在記憶體中的儲存方式、訪問方式、(類是儲存在堆上的引用型別,而結構是儲存在棧上的值型別)和他們的一些特徵(如結構不支援繼承 )。

            較小的資料型別使用結構可提高效能。

            類和結構都使用關鍵字new來宣告例項:這個關鍵字建立物件並對其進行初始化。

        3.2 類

            類中的資料和函式成為類的成員。

            3.2.1 資料成員

                資料成員是包含類的資料-----欄位、常量和事件的成員。資料成員可以是靜態資料。類成員總是例項成員,類成員總是例項成員,除非用static進行顯式宣告。

                欄位是與類相關的變數。

                常量與類的關聯方式同變數與類的關聯方式。使用const關鍵字來宣告常量。

                事件是類的成員、在發生某些行為(如改變類的欄位或屬性,或者進行了某種形式的使用者互動操作)時,它可以讓物件通知呼叫方。

            3.2.2 函式成員

                函式提供了操作類中資料的某些功能,包括方法、屬性、建構函式和終結器、運算子以及索引器。

                方法是與某個類相關的函式,與資料成員一樣,函式成員預設為例項成員,使用static修飾符可以把方法定義為靜態方法。

                屬性是可以從客戶端訪問的函式組,其訪問方式與訪問類的公共欄位類似。屬性的語法不同於一般函式的語法,在客戶端程式碼中,虛擬的物件被當做實際的東西。

                建構函式是例項化物件時自動呼叫的特殊函式。它們必須與所屬的類同名,且不能有返回型別。建構函式用於初始化欄位的值。

                終結器類似於建構函式,但是在CLR檢測到不再需要某個物件時呼叫它。它們的名稱與類相同,但前面有一個“~”符號。不可能預測什麼時候呼叫終結器。

                運算子執行的最簡單的操作就是加法和減法。在兩個整數相加時,嚴格地說,就是對整數使用“+”運算子。

                索引器允許物件以陣列或集合的方式進行索引。

                變數通過引用傳遞給方法時,被呼叫的方法得到的就是這個變數,所以在方法內部對變數進行的任何改變在方法退出後仍舊有效。

                變數通過值傳送給方法時,被呼叫的方法得到的是變數的一個相同的副本,在方法退出後,對變數進行的修改會丟失。

                對於複雜的資料型別,按引用傳遞的效率更高,因為在按值傳遞時,必須複製大量的資料。

                在C#中,除非特別說明,所有的引數都通過值來傳遞。

                注意字串的行為方式有所不同,因為字串是不可變的(如果改變字串的值,就會建立一個全新的字串),所以字串無法採用一般引用型別的行為方式。

                在方法呼叫中,對字串所做的任何改變都不會影響原始字串。

                通過值傳送變數是預設的,也可以使用ref迫使值引數通過引用傳送給方法。

                C#要求對傳遞給方法的引數進行初始化,無論是按值傳遞,還是按引用傳遞,任何變數都必須初始化。

                在方法的輸入引數前面加上out字首時,傳遞給該方法的變數可以不初始化。

                引數一般需要按定義的順序傳送給方法,命名引數允許按任意順序傳遞。

                在屬性定義中,get訪問器不帶任何引數,且必須返回屬性宣告的型別。也不應為set訪問器指定任何顯式引數,但是編譯器假定它帶一個引數,其型別也與屬性相同,並表示為value。屬性的訪問可以新增修飾符,如果屬性的set和get訪問器中沒有任何邏輯,就可以使用自動實現的屬性。這種屬性會自動實現後備成員變數。自動實現的屬性必須帶有兩個訪問器。

            3.4 結構

                結構是值型別,不是引用型別。它們儲存在棧中或儲存為內聯(如果它們是儲存在堆中的另一個物件的一部分),其生存期的限制與簡單的資料型別一樣。

                結構不支援繼承。

                對於結構建構函式的工作方式有一些區別,尤其是編譯器總是提供一個無引數的預設建構函式,它是不允許替換的。

                使用結構,可以指定欄位如何在記憶體中的佈局。

                雖然結構是值型別,但在語法上常常可以把他們當作類來處理。