在一般學習程式語言的過程中都會聽到物件導向程式設計(Object-oriented programming, 簡稱OOP), 這是一種基於物件(object)概念的開發方式, 能提高程式的可用性, 重複性, 擴展性等等的方式

而在強型別中的 C#, 使用 OOP 開發, 幾乎是不可避免

而要在 C# 中使用 OOP 需要了解一些基本的概念

  • 類別(class):定義 object 的模板或藍圖, 描述對象的 field 和 method, 一個 class 通常是由 field 和 method 所組成

  • 物件(object):物件就是類別(class)的實體化

  • 欄位(field):定義 class 內的變數(variable)

  • 建構函式(Constructor):當 object 被創建時自動調用的特殊方法, 用來初始化物件的狀態

  • 方法(method):class 中定義的函數, 用來描述 object 的行為


今天使用的舉例 Class(類別)範例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Students 類別
internal class Students
{
    // 欄位(field)
    public string Name;
    public int Number;
    public int Chinese;
    public int English;
    public int Math;

    // 建構函式(Constructor)
    public Students(string name, int number, int chinese, int english, int math)
    {
        this.Name = name;
        this.Number = number;
        this.Chinese = chinese;
        this.English = english;
        this.Math = math;
    }

    // 方法(method)
    private (int, double) Calc()
    {
        int total = Chinese + English + Math;
        double avg = total / 3.0;
        return (total, avg);
    }

    public void ShowInfo()
    {
        Console.WriteLine($"Name: {Name}, Number: {Number}");
    }

    public void ShowDetailedInfo()
    {
        (int totalScore, double avgScore) = Calc();
        Console.WriteLine($"Name: {Name}, Number: {Number}, Total Score: {totalScore}, Avg Score: {avgScore:F2}");
    }
}

類別(Class)

類別(class)簡單來說, 就是物件(object) 的藍圖(blueprint), 以描述學生為例, 需要先有設計圖來確定這個學生會有哪些特性和功能.

類別(Class)就類似這個設計圖, 它會定義未來學生的物件(Object)擁有的欄位(Field) 和方法(Method).

定義類別(class)的語法如下

1
2
3
4
internal class classname:
{
  statement
}

首先會有關鍵字 class, 再來是自定義的 <classname>, 類別的名稱習慣上會使用 Pasal 命名方式, 即每個單詞的首字母大寫

關鍵字 internal 簡單的說明是:無法被外部不相干的 project 所呼叫, 避免被誤修改操作等動作

範例:

1
2
3
4
5
6
7
8
9
// example 1
internal class Students{

}

// example 2
internal class MyChild{

}

物件(Object)

物件(object) 就是透過類別(class) 所實際建立的物體, 就是類別(class) 實體化的概念

class 和 object 的關係就像剛剛的學生設計圖和實際的學生

建立物件(object)的語法如下

1
object_name = classname();

範例:

1
2
// example
Students student1 = new Student();

範例中的 student1 就是使用 Students class 建立出來的 object

在 C# 中也有簡易的方式用來確認 class 和 object 互相之間的關係, 透過 is 這個 operator, 即可判斷

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
internal class Program
{
    static void Main(string[] args)
    {
        // 建立 Students class object
        Students student1 = new Students();

        Console.WriteLine(student1 is Students); // 反回 True
        Console.WriteLine(student1 is Teachers); // 返回 False
    }
}
// Students class
internal class Students
{
}
// Teachers class
internal class Teachers
{
}

欄位(Field)

欄位(field) 就是存放 object 的資料, 也可以說是設定 class 內部的變數(variable)

設定的語法如下:

1
object_name.field_name = value;

但是因為 C#是強型別的語言, 因此要透過上述方式對 field 進行賦值之前, 需要在 class 中先宣告該 field 和 type

並且在 field 前方也有些修飾符(modifier), 用來控制訪問權限, 這邊只會簡單說明 private 和 public 兩種

  • public: 可以從 class 的外部訪問

  • private: 只能在 class 的內部訪問

範例:

在 class 中先進行宣告

1
2
3
4
5
6
7
internal class Students
{
    // declare field
    public string Name; // 可以從 class 的外部訪問
    public int Number;
    private int Age;  // 只能在 Students class 內部訪問
}

創建後賦值

1
2
3
4
5
6
7
// create Students class object
Students student1 = new Students();
// Name field
student1.Name = "PawPaw";
// Number field
student1.Number = 1;
// student1.Age = 20;  // 這行會導致編譯錯誤,因為 Age 是 private

從上面可以看到, 需要先在 class 中宣告 field 和 type, 並在在建立了 object 之後, 才能對 field 進行賦值.

但是這種方式當 field 的數量開始多了之後, 就會顯得非常沒效率, 因此會建議使用待會提到的建構函式(Constructor).

而要存取 object field 的值則透過以下語法:

1
object_name.field_name
1
2
3
Console.WriteLine(student1.Name); // PawPaw
Console.WriteLine(student1.Number); // 1
// Console.WriteLine(student1.Age);  // 這行會導致編譯錯誤,因為 Age 是 private

使用 private 欄位可以防止外部直接訪問和修改可能的敏感數據.

通常我們會將 field 設為 private, 然後通過 public method 或其他方式來控制對這些 field 的訪問和修改.

建構函式(Constructor)

建構函式(Constructor) 就如上面所說明, 當 object 被創建時自動調用的特殊方法, 用來初始化物件的狀態.

簡單說就是當你初始化這個 object 的時候就會先強迫輸入所需要的 field value.

這樣有什麼好處呢?

  1. 可以增加效率, 不需要一個一個 field 給予 value

  2. 當 class 中, 有些方法(method) 需要依賴於一些 field 的 value 的時候, 可以避免因為該 field 沒有 value, 而出現錯誤

寫法: 與函數相同, 但是沒有回傳類型, 必須與類別名稱相同

在使用建構函式(Constructor)之前依然需要先進行 field 的宣告

設定的語法如下:

1
2
3
4
5
6
7
8
public Students(string name, int number, int chinese, int english, int math)
{
    this.Name = name;
    this.Number = number;
    this.Chinese = chinese;
    this.English = english;
    this.Math = math;
}

在這裡的 this, 是用來區分 class 的 field 和構建函數(constructor) 的參數(parameter), 所以範例中的意思就是此 class 的 Name field 等於傳入的 name parameter

簡單說:

  1. this.Name 明確表示為 class 中的 Name field

  2. name(沒有this)指的是構建函數(constructor) 的參數(parameter)

如下範例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Students 類別
public class Students
{
    // 欄位(field)
    public string Name;
    public int Number;
    public int Chinese;
    public int English;
    public int Math;

    // 建構函式(Constructor)
    public Students(string name, int number, int chinese, int english, int math)
    {
        this.Name = name;
        this.Number = number;
        this.Chinese = chinese;
        this.English = english;
        this.Math = math;
    }
}

// 使用 Contructor 創建物件
Students student1 = new Students("John", 1, 80, 75, 90);

範例中於建立 student1 物件(Object)的同時, 生成其 field 並且 initial 欄位值(Name, Number, Chinese, English, Math).

在這個例子中無法明顯體會到有沒有 this 的差異, 用下列的舉例可以明顯感受

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
internal class Students
{
    // 欄位(field)
    public string Name;
    public int Number;

    // 建構函式(Constructor)
    // 使用 this
    public Students(string Name, int Number)
    {
        this.Name = Name;
        this.Number = Number;
    }

    // 不使用 this
    public Students(string Name, int Number)
    {
        Name = Name;
        Number = Number;
    }
}

在這個例子中, 構建函數(constructor)的 parameter 和 field 的名稱剛好一樣:this.Name = Name;

先不說會不會出現 error, 光是在程式上就無法明確的知道是 method 內部的 variable 設定還是要對 class 的 field 賦值

使用 this 的主要原因:

  1. 清晰性: 明確指出我們在使用 class 的 field,而不是局部變量.

  2. 避免混淆: 當 parameter 和 field 名相同時(如這個例子), this 可以幫助區分它們.

  3. 好習慣: 即使參數名不同, 使用 this 也是個好習慣, 使代碼更一致和清晰.

方法(Method)

方法(method) 表示是在 class 中所定義的函數, 可以想成這個 object 所能做的行為

定義方法(Method) 的方式和定義一般的 function 很像, 都是修飾符開頭 -> 返回類型 -> 方法名稱 -> 參數類型 參數名稱

語法如下:

1
2
3
modifer return_value method_name(type type_name){
    statement
}

在 C# 中,我們可以定義不同類型的方法, 以下舉幾個不同的例子

  1. void method(不返回值的 method):
1
2
3
4
public void ShowInfo()
{
    Console.WriteLine($"Name: {Name}, Number: {Number}");
}
  1. 返回多個值的 method(使用元組):
1
2
3
4
5
6
private (int, double) Calc()
{
    int total = Chinese + English + Math;
    double avg = total / 3.0;
    return (total, avg);
}

這個 Calc method 會計算總分和平均分, 並以 tuple 的形式返回這兩個值. tuple 是 C# 中一種可以包含多個值的 data structure

  1. 調用其他 method 的 method:
1
2
3
4
5
public void ShowDetailedInfo()
{
    (int totalScore, double avgScore) = Calc();
    Console.WriteLine($"Name: {Name}, Number: {Number}, Total Score: {totalScore}, Avg Score: {avgScore:F2}");
}

這個 ShowDetailedInfo 方法調用了 Calc 方法, 並使用其返回值來顯示更詳細的學生信息.

底下使用一個完整的範例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
internal class Students
{
    // 欄位(field)
    public string Name;
    public int Number;
    public int Chinese;
    public int English;
    public int Math;

    // 建構函式(Constructor)
    public Students(string name, int number, int chinese, int english, int math)
    {
        this.Name = name;
        this.Number = number;
        this.Chinese = chinese;
        this.English = english;
        this.Math = math;
    }

    // private method:計算總分和平均分
    private (int, double) Calc()
    {
        int total = Chinese + English + Math;
        double avg = total / 3.0;
        return (total, avg);
    }

    // public method:顯示基本信息
    public void ShowInfo()
    {
        Console.WriteLine($"Name: {Name}, Number: {Number}");
    }

    // public method:顯示詳細信息
    public void ShowDetailedInfo()
    {
        (int totalScore, double avgScore) = Calc();
        Console.WriteLine($"Name: {Name}, Number: {Number}, Total Score: {totalScore}, Avg Score: {avgScore:F2}");
    }
}

// 使用示例
Students student1 = new Students("PawPaw", 1, 90, 80, 70);
student1.ShowInfo();         // 輸出:Name: PawPaw, Number: 1
student1.ShowDetailedInfo(); // 輸出:Name: PawPaw, Number: 1, Total Score: 240, Avg Score: 80.00

在這個例子中:

  1. Calc 是一個 private method, 只能在 class 內部使用. 它計算總分和平均分.

  2. ShowInfo 是一個簡單的公 public method,顯示學生的基本信息.

  3. ShowDetailedInfo 是另一個 public method, 它調用了 Calc method 來獲取總分和平均分, 然後顯示更詳細的學生信息.

通過這種方式, 我們可以將複雜的邏輯(如計算分數)封裝在私有方法中, 而將對外展示的功能放在 public method 中. 這種做法提高了代碼的可讀性和可維護性.