C# Notizen 3 理解C#類和對象

類是一個重要的C#編程概念,它在一個單元內定義了表示和行為。類提供了面向對象編程和面向組件編程所需的語言支持,是創建用戶定義的類型時使用的主要機制。傳統上,在面向對象編程語言中,術語“類型”指的是行為;而在面向值的編程語言中,該術語指的是數據表示。在C#中,該術語指的是數據表示和行為。這是通用類型系統的基礎,意味著當且僅當兩種類型在表示和行為方面兼容時,它們在賦值方面才是兼容的。

一、面向對象編程
ps:易于維護的代碼
當然,要創建出易于維護、理解和擴展,除確保模型正確外,還要做很多事。例如,還必須確保實現正確、易于理解、條理清晰。

類是重要的C#編程概念,它在一個單元內定義了表示和行為。換句話說,類是一種數據結構,融數據和操作數據的方法于一體。類不過是另一種數據類型,可以像使用預定義類型那樣使用它們。類為創建用戶定義的類型提供了主要機制。

面向對象編程的4個主要概念是封裝、抽象、繼承和多態。

封裝讓類能夠隱藏內部實現細節,以免遭受不希望的修改,進而導致內部狀態無效或不一致。因此,封裝有時也被稱為數據隱藏。

通過隱藏內部細節或數據,可以創建一個公有接口(抽象),它表示類的外部細節。這個接口描述了類可執行哪些操作以及類的哪些信息是公有的。只要公有接口不變,以任何方式改變內部細節都不會影響其他依賴于它的類和代碼。

通過讓類的公有接口較小,并讓類與它要表示的實際對象極其相似,可確保它對其他需要使用它的程序員來說是熟悉的。

二、面向組件編程
面向組件編程是一種軟件開發方法,它將現有的組件和新組件組合起來,就像將零件組裝成汽車一樣。軟件組件是獨立的自我描述功能包,其中包含暴露行為和數據的類型的定義。
C#通過屬性、方法、事件和特性(元數據)等概念支持面向組件編程,讓您能夠創建自我描述的獨立功能組件,這些功能組件稱為程序集。

三、C#類
在 C#中,類是隱式地從 object 派生而來的引用類型。要定義類,可使用關鍵字 class。
類體(body)是在左大括號和右大括號內定義的,您在其中定義類的數據和行為。

四、作用域和聲明空間
作用域是可使用某個名稱的范圍,而聲明空間是名稱是唯一的范圍。作用域和聲明空間緊密相連,但它們之間有一些細微的差別。

正規的定義是,作用域是一個封閉的區域,在其中無需通過限定就能使用某個名稱。這意味著命名空間、類、方法和屬性都是作用域和聲明空間,因此作用域可彼此嵌套和重疊。
既然作用域定義了名稱的可見性,且可以相互重疊,那么在外部作用域中定義的名稱在內部作用域中是可見的,反之則不成立。

如下代碼中,字段age的作用域為整個Contact類,包括F和G的函數體。在F的函數體中,age指的是字段age。

    class Contact
    {
        public int age;

        public void F()
        {
            age = 19;
        }

        public void G()
        {
            int age;
            age = 24;
        }
    }

在函數G內,作用域發生了重疊,因為有一個名為age的局部變量,其作用域為函數G的整個函數體。在函數G內,當您引用age時,引用的實際上是局部變量age,而不是在外部作用域中定義的字段age。在內部作用域內,在外部作用域內聲明的同名實體被隱藏。
使用虛線框指出了作用域邊界:

另一方面,聲明空間指的是這樣一個封閉區域,即其中不能有兩個同名的實體。例如,在Contact類中,不能再有兩個名為age的實體,除非將其中一個放在函數F或G的函數體內。同樣,在函數G內,不能再聲明一個名為age的實體。
如果將所有同名的重載方法視為一個實體,則“在聲明空間內名稱必須唯一”這一規則仍適用。

4.1 訪問性
訪問性讓您能夠控制實體在其作用域外的可見性(訪問級別)。在C#中,這是通過訪問修飾符實現的,訪問修飾符指定了在類的外部可如何訪問其成員,有時甚至對繼承進行了限制。允許訪問的類成員是可訪問的,而不允許訪問的類成員是不可訪問的。

這些訪問修飾符遵循一組簡單的規則,這些規則決定了訪問級別。

  • 對于命名空間,不能指定訪問修飾符,它們總是public(公有)。
  • 類的訪問級別默認為internal(內部),但可將其聲明為public或internal。嵌套類(在另一個類中定義的類)的訪問級別默認為 private(私有),但可將其聲明為 5 種訪問級別中的任何一種。
  • 類成員的訪問級別默認為private,但可將其聲明為5種訪問級別中的任何一種。

ps:顯式地聲明訪問級別
雖然 C#提供了的默認訪問修飾符是合理的,但您應始終顯式地聲明類成員的訪問級別。這樣可避免二義性,指出選擇是有意做出的,還可起到自我描述的作用。

C#支持的訪問修飾符:

Paste_Image.png

ps:protected internal
使用 protected internal時要小心,因為其實際效果要么是 protected,要么是internal,C#沒有提供protected且internal的概念。

4.2 字段和常量
字段是這樣的變量,即它表示與類相關聯的數據。換句話說,字段是在類的最外層作用域內定義的變量。
對于這兩種字段,都可使用5個訪問修飾符中的任何一個。通常,字段是private的,這是默認設置。
如果聲明字段(不管是實例字段還是靜態字段)時沒有指定初始值,就將根據其類型賦給相應的默認值。
與字段類似,聲明常量時也可使用5個訪問修飾符中的任何一個。常量必須有在編譯階段能夠計算出來的值,因此必須在聲明常量的同時賦值。常量必須有在編譯階段能夠計算出來的值,這種要求的好處之一是常量可依賴于其他常量。
常量通常是值類型或字面字符串,因為除string外,要創建其他引用類型的非null值,唯一的方法是使用new運算符,但這是不允許的。

ps:常量應該是恒定不變的
創建常量時,應確保它從邏輯上說是恒定不變的。好的常量應永遠不變,如Pi的值、Elvis的出生年份、1摩爾包含的分子數。

如果要創建行為類似于常量的字段,但其類型是常量聲明中不允許的,可使用修飾符static和readonly將其聲明為只讀的靜態字段。要初始化只讀字段,要么在聲明中進行,要么在構造函數中進行。

4.3 屬性
鑒于字段表示狀態和數據,但通常是私有的,必須有一種機制讓類能夠向外提供這些信息。知道各種訪問級別后,可能想將字段的訪問級別聲明為public。
這樣做可滿足抽象規則,但違反了封裝規則,因為這導致可從類外部直接操作字段。那么,如何才能同時滿足封裝規則和抽象規則呢?我們需要這樣的東西:其訪問語法與字段相同,但訪問級別不同于字段。屬性正好能夠滿足這種需求。屬性提供了一種訪問字段(支撐字段,backing field)的簡單方式,它是公有的,同時讓我們能夠隱藏字段的內部細節。就像字段可以是靜態的一樣,屬性也可以是靜態的,這種屬性不與特定的類實例相關聯。
字段被聲明為變量,因此需要占用內存空間,但屬性不需要。屬性是使用訪問器聲明的,訪問器讓您能夠控制值是否可讀寫以及讀寫時將發生的情況。get訪問器用于讀取屬性值,而set訪問器用于寫入值。

如下代碼說明了聲明屬性的最簡單方法,這種語法稱為自動實現的屬性(automatic property)。使用這種語法時,無需聲明支撐字段,必須同時包括get和set訪問器,但無需提供它們的實現,而由編譯器提供。

    class Contact
    {
        public string FirstName
        {
            get;
            set;
        }
    }

事實上,對于上圖所示的代碼,編譯器將把它們轉換為類似于下圖代碼的形式

    class Contact
    {
        private string firstName;

        public string FirstName
        {
            get
            {
                return this.firstName;
            }
            set
            {
                this.firstName = value;
            }
        }
    }

ps:自動實現的屬性
自動實現的屬性很方便,尤其是在需要實現大量屬性時。然而,這種方便也需要付出輕微的代價。
由于沒有提供訪問器,因此無法指定訪問器的任何邏輯。另外,使用自動實現的屬性語法時,必須聲明兩個訪問器。如果以后發現需要給其中一個訪問器指定邏輯,就必須添加支撐字段,并給兩個訪問器都提供合適的邏輯。
所幸的是,這種修改不會影響類的公有接口,因此可安全地進行,雖然修改起來可能有些繁瑣。
get訪問器使用一條return語句,該語句命令訪問器返回指定的值。在上圖中, set訪問器將字段firstName設置為value的值, value是一個上下文關鍵字。用于屬性的set訪問器中時,關鍵字value總是意味著“調用者提供的值”,且其類型與屬性的類型相同。

默認情況下,屬性訪問器繼承屬性定義指定的訪問級別,但可為訪問器get或set指定更嚴格的訪問級別。
還可創建計算得到的屬性(calculated property),這種屬性是只讀的,且沒有支撐字段。計算得到的屬性非常適合用于提供從其他信息派生而來的數據。

如下代碼演示了一個名為 FullName 的計算得到的屬性,它將字段 firstName 和lastName合并在一起。

    class Contact
    {
        private string firstName;
        private string lastName;

        public string FullName
        {
            get
            {
                return this.firstName + " " + this.lastName;
            }
        }
    }

ps:只讀屬性和只寫屬性
對于顯式地聲明的屬性,可省略兩個訪問器之一。通過只提供get訪問器,可創建只讀屬性;使用自動實現的屬性時,要使其成為只讀的,可將set訪問器聲明為private。
通過只提供set訪問器或將get訪問器聲明為private,可創建只寫屬性。實際上,應避免創建只寫屬性。
由于屬性訪問起來就像是字段一樣,因此在訪問器中執行的操作應盡可能簡單。如果需要執行復雜、耗時或昂貴(占用大量資源)的操作,最好使用方法而不是屬性。

4.4 方法
如果說字段和屬性定義并實現了數據,那么方法(也稱為函數)就定義并實現了可執行的行為或動作。在本書前面的示例和練習中,您一直在使用Console類的WriteLine動作,它就是一個方法。
如下代碼演示了如何在Contact類中添加一個方法,它驗證電子郵件地址。在這里,方法VerifyEmailAddress的返回類型為void,這意味著它不返回值。

    class Contact
    {
        public void VerifyEmailAddress(string emailAddress);
    }

這個方法的返回類型被聲明為bool。

如下代碼聲明一個返回值的方法

    class Contact
    {
        public bool VerifyEmailAddress(string emailAddress)
        {
            return true;
        }
    }

在方法聲明中,可指定5個訪問修飾符中的任何一個。除訪問修飾符外,還可給方法指定修飾符 static。就像靜態屬性和靜態字段不與類實例相關聯一樣,靜態方法也是如此。在Console類中,方法WriteLine就是靜態的。
方法可接受零或多個參數(輸入),參數是使用形參列表(formal parameter list)聲明的,該列表由一個或多個用逗號分隔的參數組成。對于每個參數,都必須指定其類型和標識符。如果方法不接受任何參數,就必須指定空參數列表。

參數分為3類,如下所示:

  • 值參數:這種參數最常見。調用方法時,對于每個值參數,都將隱式地創建一個局部變量,并將參數列表中相應參數的值賦給它。

ps:參數數組
參數數組是使用關鍵字params聲明的,可將其視為特殊的值參數,它聲明單個參數,但在參數列表中,它包含零或多個參數。
在方法的形參列表中,只能包含一個參數數組,且必須位于參數列表的末尾。參數數組也可以是方法的唯一一個參數。

  • 引用參數:不額外占用內存空間,而指向參數列表中相應參數的存儲位置。引用參數是使用關鍵字ref聲明的,在形參列表和實參列表中都必須使用該關鍵字。
  • 輸出參數:類似于引用參數,但在形參列表和實參列表中都必須使用關鍵字 out。與引用參數不同的是,在方法返回前,必須給輸出參數賦值。

要讓方法對對象執行所需的動作,必須調用它。如果方法需要輸入參數,就必須在實參列表中指定它們。如果方法提供輸出值,那么這個值也可存儲在變量中。
實參列表與形參列表之間通常存在一對一的關系,這意味著調用方法時,對于每個形參,都必須按正確的順序提供類型合適的值。

ps:將方法作為輸入
返回值的方法以及屬性也可用作其他方法的輸入,只要返回類型與參數類型兼容。這極大地提升了方法和屬性的用途,能夠將方法調用或屬性串接起來,形成更復雜的行為。
在前面的示例中,有一個返回類型為void的方法VerifyEmailAddress,可這樣調用它:
Contact c = new Contact();
c.VerifyEmailAddress("joe@example.com");
然而,對于返回類型為bool的方法VerifyEmailAddress,可這樣調用它:
Contact c = new Contact();
bool result = c.VerifyEmailAddress("joe@example.com");
就像形參列表一樣,調用不需要參數的方法時,也必須指定一個空列表。

方法重載
通常,在同一個聲明空間內,不能有兩個同名的實體,但重載方法除外。在同一個聲明空間內,如果多個方法同名但簽名(signature)不同,那么它們就是重載的。
方法簽名由方法名以及形參的數量、類型和修飾符組成,必須與同一個類中聲明的其他方法簽名都不同;另外,方法也不能與類中聲明的其他所有非方法實體同名。

ps:方法簽名
返回類型并非方法簽名的一部分,因此兩個方法不能只有返回類型不同。
雖然形參列表是方法簽名的一部分,但不能因為某個參數為ref或out就認為兩個方法不同。判斷方法簽名是否相同時,不考慮參數的ref或out特性。
重載方法時,只有改變簽名。更準確地說,只能改變參數的數量和類型。對于前面使用過的方法Console.WriteLine,它有19個重載版本供您選擇。
在.NET Framework中,方法重載很常見。這讓您能夠像類用戶提供單個方法,但用戶與之交互時可提供不同的輸入。編譯器將根據輸入決定使用哪個重載版本。

ps:利用不同的返回類型進行重載
您可能利用不同的返回類型進行重載,雖然這可能是合法的 C#代碼,但是由于方法簽名不包含返回類型,因此這可能導致混亂。為最大限度地減少混亂,應避免這樣做。
在需要提供多種執行動作的方式時,方法重載很有用,但是可供選擇的空間太大時,可能難以應付。
如下是一個方法重載示例:

public void Search(float latitude, float longitude)
{
    Search(latitude, longtitude, 10, "en-US");
}

public void Search(float latitude, float longitude, internal distance)
{
    Seach(latitude, longitude, distance, "en-US");
}

public void Search(float latitude, float longitude, internal distance, string culture)
{

}

可選參數和命名參數
可選參數讓您能夠在調用方法時省略相應的實參。只有值參數可以是可選的,所有可選參數都必須位于必不可少的參數后面,且位于參數數組前面。
要將參數聲明為可選的,只需給它提供默認值。下述修改后的Search方法使用了可選參數。
public void Search(float latitude, float longitude, int distance = 10,
string culture = "en-US");
其中,參數latitude和longitude是必不可少的,而參數distance和culture都是可選的。使用的默認值與第一個重載的Search方法提供的值相同。
從前一節的Search方法重載可知,參數越多,需要提供的重載版本越多。在這里,只有幾個重載版本,但是使用可選參數時只需要一個版本。在有些情況下,重載是唯一的辦法,尤其是在參數沒有合理的默認值時,但是很多情況下可使用可選參數達到同樣的目的。

ps:可選參數和必不可少的參數
有默認值的參數為可選參數,沒有默認值的參數為必不可少的參數。
集成非托管編程接口(如Office自動化API)時,可選參數很有用,這些接口在編寫時考慮到了可選參數。在這些情況下,原始API可能接受大量的參數(有時多達30個),但大部分參數都有合理的默認值。
調用方法時,可以不顯式地給可選參數提供實參,這將使用其默認值。然而,如果調用方法時給可選參數提供了實參,就將使用該實參而不是默認值。

可選參數的缺點:不能使用兩個逗號來表示省略了實參
為解決這種問題,C#允許按名稱傳遞實參,這能夠顯式地指定實參之間的關系以及實參對應的形參。
如下代碼是使用實參的示例

Paste_Image.png

這些調用都是等價的。前3個調用只是顯式地制定了每個參數。最后兩個調用演示了如何在參數列表中省略實參,它們是等價的,只是其中一個混合使用了命名實參和位置實參

ps:不是按名稱傳遞的實參稱為位置實參(positional argument)。位置實參最常用。
通常在有可選參數時使用命名實參,但沒有可選參數時也可使用命名實參。不同于可選參數,命名實參可用于值參數、引用參數和輸出參數;還可將其用于參數數組,但必須顯式地聲明一個數組來存儲值,如下所示:
Console.WriteLine(String.Concat(values: new string[] { "a", "b", "c" }));
正如您從Search方法看到的,通過顯式地指出實參的名稱,C#提供了另一種功能強大的方法,讓你能夠編寫含義不言自明的代碼。

五、實例化類
使用預定義類型時,只需聲明變量并給它賦值,而要在程序中使用類,必須創建示例。
雖然直接使用關鍵字new創建對象,但在后臺、虛擬執行系統將負責分配所需的內存,垃圾收集器將負責釋放內存

要實例化類,可使用關鍵字new,如下所示:
Contact c = new Contact();

對于新創建的對象,必須制定初始狀態,這意味著對于聲明的每個字段,都必須顯式地提供初始值,否則它將使用默認值
有時這種初始化足夠了,但通常還不夠。為在初始化階段執行其它操作,C#提供了實例構造函數(有時簡稱構造函數),這是一個特殊的方法,每當創建實例時都自動執行。
構造函數與類同名,但不能返回值,這不同于返回void的方法。如果構造函數沒有參數,就是默認構造函數。

ps:默認構造函數
每個類都必須至少有一個構造函數,但你并非總是需要編寫它。如果沒有提供任何構造函數,C#將創建一個默認構造函數。這個構造函數實際上什么也不是,但確實存在。
僅當沒有提供任何構造函數時,編譯器才會生成默認構造函數,這讓你一不小心就會破壞類的公有接口:添加了接受參數的構造函數,卻忘記顯式地添加默認構造函數。因此,最好總是提供默認構造函數,而不是讓編譯器生成。
默認構造函數(以及其他任何構造函數)可使用任何訪問修飾符,因為完全可以創建私有默認構造函數。如果要允許實例化類,同時要確保創建其對象時都提供某些信息,這將很有用。

如下列出了Contact類的默認構造函數

public class Contact
{
    public Contact()
    {

    }
}

就像可以重載常規方法一樣,構造函數也可以重載。與常規方法一樣,重載的構造函數的簽名不能相同。

一些提供特殊構造函數的原因:

  • 使用默認構造函數創建的對象的初始狀態不合理
  • 提供初始狀態既方便又合理
  • 創建對象的開銷可能很大,因此想確保對象的初始狀態是正確的
  • 非公有構造函數可限制使用它來創建對象的權限

如下聲明一個重載構造函數

public class Contact
{
    public Contact(string firstName, string lastName, DateTime dateOfBirth)
    {
        this.firstName = firstName;
        this.lastName = lastName;
        this.dateOfBirth = dateOfBirth;
    }
}

如上重載構造函數中,將參數的值賦給了相應的私有字段

當類包含多個構造函數時,通常將它們串接(chain together)起來,但并非總是這樣做。要串接構造函數,可使用包含關鍵字this的特殊語法、

ps:關鍵字this
關鍵字 this表示類的當前實例,它類似于Visual Basic關鍵字Me、F#標識符self、Python特性(attribute)self和Ruby中的self。
this關鍵字的常見用途如下:

  • 限定被相似名稱隱藏的成員
  • 將對象作為參數傳遞給其他方法
  • 在構造函數中指定要調用哪個構造函數
  • 在擴展方法中指定要擴展的類型

由于靜態成員與類相關聯,而不與實例相關聯,因此不能使用關鍵字this來引用它。
在聲明一個重載構造函數的源碼中,關鍵字 this 用于將類字段和參數區分開來,因為它們的名稱相同。

如下代碼使用了構造函數串接

public class Contact
{
    public Contact()
    {

    }

    public Contact(string firstName, string lastName, DateTime dateOfBirth)
        :this()
        {
            this.firstName = firstName;
            this.lastName = lastName;
            this.dateOfBirth = dateOfBirth;
        }
}

構造函數串接的優點之一是,可串接類中的任何構造函數,而不僅僅是默認構造函數。使用構造函數串接時,明白構造函數的執行順序很重要。將沿構造函數鏈前行,直到到達串接的最后一個構造函數,然后沿鏈條從后往前執行構造函數。在下面所示的C類中,有3個構造函數,每個構造函數都串接到默認構造函數。
如下代碼說明了串接構造函數的執行順序

public class C
{
    string c1;
    string c2;
    int c3;

    public C()
    {
        Console.WriteLine ("Default constructor");
    }

    public C(int i, string p1) : this(p1)
    {
        Console.WriteLine (i);
    }

    public C(string p1) : this()
    {
        Console.WriteLine (p1);
    }
}

下圖說明了使用第二個構造函數(它接受一個int參數和一個string參數)實例化對象時,構造函數的執行順序。

Paste_Image.png

靜態構造函數
在有些情況下,類可能要求特殊的初始化操作,這種操作最多執行一次:訪問實例成員之前。
為此,C#提供了靜態構造函數,其形式與默認構造函數相同,但是不使用訪問修飾符,而是使用修飾符 static。由于靜態構造函數初始化類,因此不能直接調用靜態構造函數。
靜態構造函數最多執行一次:首次創建實例或首次引用靜態類成員時。

六、嵌套類
嵌套類(nested class)完全封裝(嵌套)在另一個類的聲明中。嵌套類提供了一種方便的方法,讓外部類能夠創建并使用其對象,但在外部類的外面不能訪問它們。雖然嵌套類很方便,但也容易濫用,這可能導致類難以處理。
嵌套類的訪問級別至少與包含它的類相同。例如,嵌套類為 public,而包含它的類為internal,則嵌套類的訪問級別默認也為internal,只有所屬程序集的成員能夠訪問它。然而,如果包含它的類為public,嵌套類遵循的訪問級別規則將與非嵌套類相同。
在下述情況下應考慮將類實現為嵌套類:它本身沒有意義,且從邏輯上說可包含在另一個類內或其成員需要訪問另一個類的私有數據。嵌套類通常不應是公有的,因為它們僅供在包含它的類中使用。

七、分部類
分部類(partial class)能夠將類聲明分成多個部分—通常存儲在多個文件中。分部類的實現方法與常規類完全相同,但在關鍵字class前面有關鍵字partial。使用分部類時,其所有部分都必須在編譯階段可用,且訪問級別相同,這樣才能組成完整的類。
代碼生成工具(如Visual Studio的可視化設計器)大量地使用了分部類,該設計器為您生成類,用于表示你設計的可視化控件。機器生成的代碼單獨放在分部類的一個部分中,這樣你可以修改分部類的另一部分,而不用擔心重新生成機器生成的部分時,所做的修改會丟失。
在不涉及機器生成代碼的情形下,也可使用分部類。聲明大型類時,可受益于使用分部類,但有時候這意味著類的功能太多了,最好將其分成多個類。
ps:嵌套類和分部類
雖然C#不像Java那樣要求每個類一個文件,但是這樣做通常是有好處的。使用嵌套類時,除非包含它的類是分部類,否則根本無法實現每個類一個文件的目標。

八、靜態類
到目前為止,你知道修飾符static可用于構造函數、字段、方法和屬性;修飾符static也可用于類,這將定義靜態類。靜態類只能有一個構造函數且是靜態的,因此不可能創建靜態類的實例。有鑒于此,靜態類通常包含實用程序或輔助方法(helper method),它們不需要類實例就能工作。

ps:隱式靜態成員
靜態類只能包含靜態成員,但這些成員并不會自動變成靜態的,必須顯式地使用修飾符 static。然而,可將任何靜態成員聲明為 public、private或internal的。
擴展方法是常規的靜態方法,但第一個參數包含修飾符this,該參數指定要擴展的類型,通常稱為類型擴展參數。擴展方法必須在非嵌套、非泛型靜態類中聲明。
由于擴展方法不過是經過特殊標記的靜態方法,因此它對被擴展的類型沒有特殊訪問權限,而只能通過該類型的公有接口訪問其成員。另外,調用擴展方法時,需要使用更傳統的方式—使用其全限定名。

ps:訪問internal成員
如果擴展方法是在被擴展的類型所屬的程序集中定義的,那么它也能夠訪問該類型的internal成員。
雖然擴展方法的簽名可以與被擴展類型的實際方法相同,但是這樣的擴展方法將不可見。解析方法時,編譯器確保實際類方法優先于擴展方法,這樣可禁止擴展方法改變標準類方法的行為,因為這種改變將導致無法預料(至少是意外)的行為。

九、對象初始值設定項
前面介紹了如何創建構造函數,為設置初始狀態提供一種方便的方法。然而,與方法重載一樣,需要設置的字段越多,可能需要提供的重載構造函數也越多。雖然構造函數支持可選參數,但是有時您想在創建對象實例時設置屬性。
類提供了對象初始值設置項語法,能夠在調用構造函數的同時設置公有字段或屬性。這提供了極大的靈活性,可極大地減少需要提供的重載構造函數。

如下代碼使用了對象初始值設定項

    Contact c1 = new Contact();
    c1.FisrtName = "Carl";
    c1.LastName = "Doenitz";
    c1.DateOfBirth = new DateTime(1992,08,15);
    Console.WriteLine(c1.ToString());

    Contact c2 = new Contact
    {
        FistName = "Carl",
        LastName = "Doenitz",
        DateOfBirth = new DateTime(1992,08,15);
    };

    Console.WriteLine(c2.ToString());

只要字段或屬性之間不存在依存關系,對象初始值設定項就是一種簡介的方法,可用于同時實例化和初始化對象。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,460評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,067評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,467評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,468評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,184評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,582評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,616評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,794評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,343評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,096評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,291評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,863評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,513評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,941評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,190評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,026評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,253評論 2 375

推薦閱讀更多精彩內容