CodeGym /課程 /C# SELF /建構子多載

建構子多載

C# SELF
等級 16, 課堂 4
開放

1. 前言

假設我們有一個 class Person:

public class Person
{
    public string Name;
    public int Age;
    
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

你現在感覺自己快變天才了——可以創建任何名字和年齡的人。但有時候資訊不夠啊!App 的使用者只輸入了名字,年齡之後才知道。或者你有時候想給個「預設」年齡。當然可以想些 workaround,但 C# 給你一個超優雅的解法——建構子多載。

建構子多載 就是說一個 class 可以有多個同名建構子(就是 class 名),但參數列表不一樣。

這些參數列表有什麼差別? 可以差在:

  1. 參數數量: 比方說一個建構子收 2 個參數,另一個收 3 個。
  2. 參數型別: 一個收 (string, int),另一個收 (string, decimal)。
  3. 參數順序: (string, int) 跟 (int, string) 是不同的 signature。

最重要的規則:compiler 會根據signature 來分辨建構子(還有任何 method)。Signature 包含建構子(或 method)名稱和參數列表(數量、型別、順序)。存取修飾詞(public、private)還有一般 method 的回傳型別都不算 signature 的一部分。但建構子沒有回傳型別,名稱也一定跟 class 一樣。

2. 建構子多載:語法跟範例


public class Cat
{
    public string Name;
    public string Color;

    // 無參數建構子
    public Cat()
    {
        Name = "無名貓";
        Color = "灰色";
    }

    // 一個參數的建構子
    public Cat(string name)
    {
        Name = name;
        Color = "灰色";
    }

    // 兩個參數的建構子
    public Cat(string name, string color)
    {
        Name = name;
        Color = color;
    }
}

用法:

Cat barsik = new Cat(); // "無名貓", "灰色"
Cat murzik = new Cat("Murzik"); // "Murzik", "灰色"
Cat ryzhik = new Cat("Ryzhik", "橘色"); // "Ryzhik", "橘色"

你在 new 物件時有給參數,C# 會根據參數數量跟型別「猜」你要哪個建構子。

超方便!用你 class 的人不用填他不知道的參數:只想要一隻貓——用無參數建構子,知道名字——用另一個,有顏色需求——用第三個。

3. 多載建構子的呼叫怎麼運作?

C# 會在 compile 階段自動選對的建構子,根據你傳的參數型別和數量。很難搞錯——如果你搞錯,compiler 馬上兇你。

來加個只收 int 參數的建構子:

public Cat(int age)
{
    Name = "無名貓";
    Color = "灰色";
    // 這裡可以加年齡的邏輯
}

現在你還可以「用年齡創貓」:

Cat oldCat = new Cat(5); // 會呼叫 Cat(int age) 這個建構子
建構子呼叫 會呼叫哪個?
new Cat()
無參數
new Cat("Murka")
一個 string 參數
new Cat("Ryzhik", "橘色")
兩個 string 參數
new Cat(10)
一個 int 參數

4. 建構子內部呼叫:this 關鍵字

有時候不同建構子會做很像(甚至一樣)的事。為了不複製貼上,可以「一個建構子呼叫另一個」。這時就用 this。


public class Cat
{
    public string Name;
    public string Color;

    public Cat() : this("無名貓")
    {
        // 這個建構子會呼叫 Cat(string name)
    }

    public Cat(string name) : this(name, "灰色")
    {
        // 這個建構子會呼叫 Cat(string name, string color)
    }

    public Cat(string name, string color)
    {
        Name = name;
        Color = color;
    }
}

結果:

  • new Cat() 會呼叫 Cat(string name),然後再呼叫 Cat(string name, string color)。
  • 所有「路都通羅馬」:也就是所有初始化都集中在一個「主建構子」。

這種寫法叫 constructor chaining(建構子串接)。

5. 生活中的例子

來寫個遊戲裡的英雄 class(Hero),有名字跟等級!這樣寫:

只有一個建構子:

public class Hero
{
    public string Name;
    public int Level;

    public Hero(string name, int level)
    {
        Name = name;
        Level = level;
    }
}

現在讓我們可以用不同方式創英雄!

多個建構子:

public class Hero
{
    public string Name;
    public int Level;

    // 如果沒給,預設是「無名」1級
    public Hero() : this("無名", 1)
    {
    }

    // 只知道名字,等級預設 1
    public Hero(string name) : this(name, 1)
    {
    }

    // 最主要的建構子,兩個參數
    public Hero(string name, int level)
    {
        Name = name;
        Level = level;
    }
}

現在可以這樣創英雄:

Hero h1 = new Hero();               // "無名", 1
Hero h2 = new Hero("Arthur");        // "Arthur", 1
Hero h3 = new Hero("Laura", 10);     // "Laura", 10

6. 實作細節:隱藏的地雷跟小細節

第一: 如果你自己寫了任何一個建構子,compiler 就不會自動幫你生「空建構子」。所以如果你只寫了有參數的建構子,沒寫無參數的話,像這樣:

Hero h = new Hero(); // 沒有無參數建構子會報錯!
會直接 compile error。

第二: 如果串接建構子時呼叫錯了,初始化邏輯就會亂掉。要保持一致性:最好所有實際初始化都在最完整的建構子,其他都用 this(...) 傳資料進去。

第三: 如果參數只差型別(像 Cat(string s) 跟 Cat(object o)),你用 null 當參數時可能會搞混。compiler 不一定知道你想要哪個建構子。

7. 多載跟欄位初始化

class 裡常常有些欄位一定要給值。建構子多載可以讓你很方便地做到,但預設值要你自己決定什麼最合理。

加個檢查的範例:

public class Book
{
    public string Title;
    public int Year;

    // 預設書名「無標題」,年份 2000
    public Book() : this("無標題", 2000)
    {
    }
    
    public Book(string title) : this(title, 2000)
    {
    }

    public Book(string title, int year)
    {
        Title = title;
        Year = year;
    }
}

8. 多載有不同參數組合

有些 class 建構子多到數十個!這不一定是好設計(有時候用「設定」method 或 builder pattern 會更好),但對於 user object、model、DTO——這樣很實用。

範例:class「寵物」,你可以全都指定,也可以只給必要的。

public class Pet
{
    public string Name;
    public int Age;
    public string Type;
    public bool IsVaccinated;

    // 預設建構子
    public Pet() : this("NoName", 0, "Cat", false) { }

    // 最少資訊
    public Pet(string name, string type) : this(name, 0, type, false) { }

    // 完整建構子
    public Pet(string name, int age, string type, bool isVaccinated)
    {
        Name = name;
        Age = age;
        Type = type;
        IsVaccinated = isVaccinated;
    }
}
方法多載跟建構子多載的差別
一般方法 建構子(包含多載)
隨時都能呼叫 只有 new 物件時會呼叫(new)
可以回傳任何型別 永遠不回傳值(不用寫型別)
方法名稱隨便取,通常用動詞 名稱一定跟 class 一樣
可以用參數多載 建構子也是用參數多載
2
任務
C# SELF, 等級 16, 課堂 4
上鎖
建立具有多載建構子的類別
建立具有多載建構子的類別
2
任務
C# SELF, 等級 16, 課堂 4
上鎖
透過 `this` 呼叫建構子
透過 `this` 呼叫建構子
1
問卷/小測驗
類別與物件的概念,等級 16,課堂 4
未開放
類別與物件的概念
類別與建構子
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION