C#學習筆記第二篇

面向對象編程

OOP三大特性:封裝,繼承,多態。封裝就是把客觀事物封裝成抽象的類,並且類可以把自己的數據和方法只讓可信的類或對象操作,對不可信的進行信息隱藏。在C#中,OOP技術的一個應用就是WPF桌面應用程序。

定義類

對象是OOP應用程序的組成部件,類是用於實例化對象的類型定義。對象可以包含數據,提供其他代碼可以使用的操作。數據可以通過屬性供外部代碼使用,操作可以通過方法供外部代碼使用。屬性和方法都稱爲類的成員。屬性可以進行讀取訪問,寫入訪問或讀寫訪問。在.NET 中,所有的東西都是對象。

C#使用 class 關鍵字來定義類:

class MyClass : MyBase, MyInterface, MySecInterface  //用逗號將基類名和接口名分隔開,如果未指定基類,接口就跟在冒號的後面。
{
    //code
}

默認情況下,類聲明爲內部的,即只有當前項目中的代碼才能訪問它。可使用 internal 訪問修飾符關鍵字來顯式地指定這一點。另外,還可以使用 public 關鍵字指定類是公共的,abstract 關鍵字指定類是抽象的(可以是公共抽象類,也可以是內部抽象類),sealed 關鍵字指定類是密封的(可以是公共密封類,也可以是內部密封類)。還可以在類定義中指定繼承,需要在類名的後面加一個冒號,其後是基類名。編譯器不允許派生類的可訪問性高於基類。如果沒有使用基類,被定義的類就只繼承於基類System.Object。類裏面還可以嵌套類。

成員定義

類定義中的所有成員(字段,方法和屬性)都有自己的訪問級別:

  • public:成員可以由任何代碼訪問,.NET Framework中的公共字段以PascalCasing形式來命名
  • private:成員只能由類中的代碼訪問(默認使用這個關鍵字)
  • internal:成員只能由定義它的程序集(項目)內部的代碼訪問
  • protected:成員只能由類或派生類中的代碼訪問
  • static/const:表示類的靜態成員,而不是對象實例的成員。

字段也可以使用關鍵字readonly,表示這個字段只能在執行構造函數的過程中賦值,或由初始化賦值語句賦值。定義方法時也可以使用下述關鍵字:virtual(方法可以重寫),abstract(方法必須在非抽象的派生類中重寫,只用於抽象類中),override(方法重寫了一個基類方法),extern(方法定義放在其他地方)。屬性也可以使用virtual, abstract, override關鍵字,但這些關鍵字不能用於字段。屬性定義方式與字段類似,但包含的內容比較多。屬性擁有兩個類似於函數的塊,一個塊用於獲取屬性的值,另一個塊用於設置屬性的值。這兩個塊也稱爲訪問器,分別用get和set關鍵字來定義,用於控制屬性的訪問級別。可以忽略其中的一個塊來創建只讀或只寫屬性。當然,這僅適用於外部代碼,因爲類中的其他代碼可以訪問這些代碼塊能訪問的數據。還可以在訪問器上包含可訪問修飾符,例如使get塊變成公共的,使set塊變成受保護的。C#6引入了一個名爲“基於表達式的屬性”的功能。例如:

private int myDoubledInt = 5;
public int MyDoubledIntProp => (myDoubledInt * 2);

重構成員

在添加屬性時有一項很方便的技術,可以從字段中生成屬性:在代碼視圖中右擊某個成員,選擇“快速操作和重構”,這樣就可以減少爲該字段創建屬性的時間。

成員隱藏與重寫

當從基類繼承一個(非抽象的)成員時,也就繼承了其實現代碼。如果繼承的成員是虛擬的,就可以用 override 關鍵字重寫這段實現代碼。如果無論繼承的成員是否爲虛擬,我們想隱藏這些實現代碼,則可以使用new關鍵字顯式地表明意圖,同時還可以通過基類訪問它,但重寫不能通過基類訪問它。

調用重寫或隱藏的基類方法

無論是重寫成員還是隱藏成員,都可以在派生類的內部訪問基類成員,可使用 base 關鍵字,它表示包含在派生類中的基類的實現代碼。例如:

public class MyBaseClass
{
    public virtual void DoSomething()
    {
        //Base implementation
    }
}
public class MyDerivedClass : MyBaseClass
{
    public override void DoSomething()
    {
        base.DoSomething();
        //Other implementation
    }
}

除了 base 關鍵字,還可以使用 this 關鍵字,只是 this 引用的是當前的對象實例(即不能在靜態成員中使用 this 關鍵字,因爲靜態成員不是對象實例的一部分)。this 的第一個常見用法是把當前對象實例的引用傳遞給一個方法,第二個常見用法是限定局部類型的成員。

類與結構

結構和類非常相似,但結構是值類型,類是引用類型。

接口

接口就是把公共實例(非靜態)方法和屬性組合起來,以封裝特定功能的一個集合。一旦定義了接口,就可以在類中實現它。接口不能單獨存在,不能像實例化一個類那樣實例化接口,接口不能包含實現其成員的任何代碼,只能定義成員本身。實現過程必須在實現接口的類中完成。接口的定義如下:

interface IMyInterface  //接口名一般以大寫字母I開頭
{
	//code
}

訪問修飾符關鍵字有 internal 和 public 兩個。與類繼承不同的是,一個類可以實現多個基接口。

IDisposable接口

如果我們想不再需要某個對象時,就釋放這個資源,則這個類必須要實現IDisposable接口中的 Dispose() 方法。C#允許使用一種可以優化使用這個方法的結構。using 關鍵字可以在代碼塊中初始化使用重要資源的對象,在這個代碼塊的末尾會自動調用 Dispose() 方法,用法如下:

<ClassName> <VariableName> = new <ClassName>();
using (<VariableName>)
{
	...
}
//或者把初始化對象作爲 using 語句的一部分:
using (<ClassName> <VariableName> = new <ClassName>())
{
	...
}

繼承

繼承是一個類定義派生於另一個類定義的機制。在OOP中,被繼承的類稱爲父類(也稱爲基類)。C#中的對象僅能直接派生於一個基類,當然基類也可以有自己的基類。基類還可以定義爲抽象類,抽象類不能直接實例化。要使用抽象類,必須繼承這個類,抽象類可以有抽象成員,這些成員在基類中沒有實現代碼,所以派生類必須實現它們。在繼承一個基類時,成員的可訪問性就成了一個重要問題。派生類可以訪問基類的公共成員和受保護的成員(外部代碼不能訪問),但不能訪問其私有成員。密封(seal)的類不能用作基類。在C#中,所有對象都有一個共同的基類 object (在 .NET Framework 中,它是 System.Object 類的別名)。

多態

繼承的一個結果就是派生於基類的類在方法和屬性上有一定的重疊,因此,可以使用相同的語法處理從同一個基類實例化的不同對象。多態性允許把某個派生類型的變量賦給基本類型的變量,之後就可以通過這個變量調用基類的方法(實際上是調用這個派生類中同名方法的實現代碼)。還可以把基本類型的變量轉換爲派生類變量,調用派生類的方法。實現多態的方法有覆蓋(運行期確定),重載(編譯期確定)。

System.Object

在繼承層次結構中,所有類的根都是 System.Object。System.Object 包含的方法在這裏不再列出。

構造函數與析構函數

每個對象都有一個明確定義的生命週期,除了“正在使用”的正常狀態外,還有兩個重要的階段:

  • 構造階段:第一次實例化一個對象時,需要初始化該對象。這個初始化過程稱爲構造階段,由構造函數完成。
  • 析構階段:在刪除一個對象時,常常需要執行一些清理工作,例如釋放內存,這由析構函數完成。

在C#中定義類時,常常不需要定義相關的構造函數和析構函數,因爲在編寫代碼時,如果沒有提供它們,編譯器會自動添加它們。但如有必要,可以提供自己的構造函數和析構函數,以便初始化對象和清理對象。

class MyClass
{
	public MyClass()
	{
		//Default constructor code
	}
	
	~MyClass()       //聲明析構函數
	{
		//code
	}
}

所有的類定義至少包含一個構造函數,構造函數分爲默認構造函數(沒有參數)和非默認構造函數(帶參數)。構造函數與字段,屬性和方法一樣,可以是公共或私有的,在類外部的代碼不能使用私有構造函數實例化對象,而必須使用公共構造函數。一些類沒有公共的構造函數,外部的代碼就不可能實例化它們,這些類稱爲不可創建的類(靜態類也是不可創建的類)。

構造函數的執行序列

在C#中,任何構造函數都可以配置爲在執行自己的代碼前調用其他構造函數。爲了實例化派生的類,必須實例化它的基類,而要實例化這個基類,又必須實例化這個基類的基類,這樣一直實例化到System.Object爲止。結果是無論使用什麼構造函數實例化一個類,總是首先調用System.Object.Object()。無論在派生類上使用什麼構造函數(默認的或非默認的),除非明確指定,否則就使用基類的默認構造函數。只需使用構造函數初始化器,就可以指定.NET 實例化過程使用基類中具有指定參數的構造函數。也可以使用 base 這個關鍵字指定基類構造函數的字面值。除了 base 關鍵字外,還可將另一個關鍵字 this 用作構造函數初始化器。注意在定義構造函數時,不要創建無限循環。例如:

public class MyBaseClass
{
    public MyBaseClass() : this(5)
    {

    }
    public MyBaseClass(int i) : this()
    {

    }
}

此時,編譯器會報錯:構造函數“MyBaseClass.MyBaseClass(int)”無法通過另一構造函數調用自身。

接口和抽象類

相同點:都包含可以由派生類繼承的成員;都不能直接實例化;派生類必須實現未實現的方法。
不同點:派生類只能直接繼承自一個抽象類,類可以使用任意多個接口,但這並不會產生太大的區別;抽象類的成員可以是私有的,受保護的,內部的或受保護的內部成員,而接口成員必須是公共的;接口不能包含字段,構造函數,析構函數,靜態成員或常量;抽象類可以擁有抽象成員(沒有代碼體,必須在派生類中實現,否則派生類本身必須也是抽象的)和非抽象成員(擁有代碼體,也可以是虛擬的,這樣就可以在派生類中重寫),而接口成員必須都在使用接口的類上實現(它們沒有代碼體)。

什麼場景使用什麼技術?
抽象類主要用作對象系列的基類,這些對象共享某些主要特性,例如共同的目的和結構。接口則主要用於類,這些類存在根本性的區別,但仍可以完成某些相同的任務。

淺度和深度複製

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章