iT邦幫忙

0

【C#學習筆記】09《類別(Class)》函式多載、靜態成員

  • 分享至 

  • xImage
  •  

這篇筆記會接續上一篇的內容,繼續講解類別相關的觀念與功能。


【C#學習筆記】08《類別(Class)》引用、繼承


多載(Overloading)

在同一個類別中,允許建立多個同名但參數不同的函式,這種機制稱為「多載 (Overloading)」。
以先前的Animal案為例,當我們在子類別Dog中新增函式時,只要讓它們根據傳入的參數數量或類型來執行不同的邏輯,就能成功實現函式多載。

// Dog Only Method
public string Play() => $"{Name} is playing with a ball";
public string Play(string toy) => $"{Name} is playing with a {toy}";
public void Play(string toy, int times)
{
    for (int i = 0; i < times; i++)
    {
        Console.WriteLine($"{Name} is playing with a {toy} {i + 1} time(s)");
    }
}
public void Play(string toy, int times, string location)
{
    for (int i = 0; i < times; i++)
    {
        Console.WriteLine($"{Name} is playing with a {toy} in the {location} {i + 1} time(s)");
    }
}
using Class_02;

var dog = new Dog("Rover", 5);
Console.WriteLine(dog.Play());
Console.WriteLine(dog.Play("frisbee"));
dog.Play("frisbee", 3);
dog.Play("frisbee", 3, "park");

類別中,函式的預設參數

在C#中,我們可以為函式的參數設定「預設值」。當呼叫函式不傳入該參數時,系統就會自動採用預設值。
延續先前的Play()函式範例,如果我們將原本用來處理多載的無參數Play()註解掉,並直接在帶參數的 Play(string item = "ball") 中加上預設值,就能用更精簡的程式碼達到相同的效果。

//public string Play() => $"{Name} is playing with a ball";
Console.WriteLine(dog.Play());//出現錯誤

預設參數翻立

public string Play(string toy = "ball") => $"{Name} is playing with a {toy}";//指定參數
public void Play(string toy = "ball", int times = 1)
{
    for (int i = 0; i < times; i++)
    {
        Console.WriteLine($"{Name} is playing with a {toy} {i + 1} time(s)");
    }
}

若同時使用多載與預設參數,需要特別注意編譯器的多載解析(Overload Resolution)限制。一旦定義了多個都帶有預設參數的同名函式,外部呼叫時若省略了引數,系統便會陷入無法判斷要調用哪一個方法成員的窘境,進而跳出編譯出錯的警告。

運算子多載

運算子多載(Operator Overloading)是OOP(物件導向)中很常見的功能。它的核心概念是「讓自訂類別,也能像內建型別一樣使用 + - == > 等運算子」
例如,這裡 + 是內建的。

int a = 3 + 5;

但如果你自己做一個Vector2類別:

Vector2 a = new Vector2(1, 2);
Vector2 b = new Vector2(3, 4);

Vector2 c = a + b;

正常情況下a + b其實不能用。
因為C#不知道「兩個 Vector2 相加到底代表什麼?」

範例:

namespace Class_02;

public class Vector
{

    public double X;
    public double Y;

    public Vector(double x, double y)
    {
        X = x;
        Y = y;
    }

    //operator overloading : +, -, ==, !=

    public static Vector operator +(Vector p1, Vector p2) 
        => new Vector(p1.X + p2.X, p1.Y + p2.Y);
    public static Vector operator -(Vector p1, Vector p2) 
        => new Vector(p1.X - p2.X, p1.Y - p2.Y);
    public static bool operator ==(Vector p1, Vector p2) 
        => p1.X == p2.X && p1.Y == p2.Y;
    public static bool operator !=(Vector p1, Vector p2) 
        => !(p1 == p2);

    public override string? ToString()
    {
        return $"({X}, {Y})";
    }

}

ToString()是用來定義當你的物件被轉成字串或被印出來時要顯示的內容,因為預設情況只會顯示類別名稱不好讀,所以你override它之後,就可以像 return $"({X}, {Y})"; 這樣讓輸出變成有意義的格式,例如座標顯示成 (3, 4),方便除錯和輸出資訊。

using Class_02;

var p1 = new Point(1, 2);
var p2 = new Point(2, 3);

var p3 = p1 + p2;
Console.WriteLine($"{p1} + {p2} = {p3}");
Console.WriteLine();

var p4 = p1 - p2;
Console.WriteLine($"{p1} - {p2} = {p4}");
Console.WriteLine();

Console.WriteLine($"{p1} == {p2} : {p1 == p2}");

常見的有:

運算子 用途
+ 相加
- 相減
* 乘法
/ 除法
== 是否相等
!= 不等
> < 比較
++ -- 遞增遞減

靜態成員(Static Member)

標註static的這個成員(變數或方法)屬於「類別本身」而不是某個物件,所以不需要建立實例(new)就可以直接用類別名稱存取,而且所有物件共用同一份static資料,代表的是整個類別層級的共用狀態或功能,而不是每個物件各自擁有一份。

namespace Class_02;

public class Cat : Animal
{

    public static int Counter {  get; set; }
    public static int DoCount() => Counter;
    public int CatCount() => Counter;
    public Cat()
    {
        Name = "Dog";
        Counter++;
    }

    public Cat(string name, int age)
    {
        Name = name;
        Age = age;
        Counter++;
    }

    public override void MakeSound()
    {
        Console.WriteLine("Meow Meow Meow");
    }

}
using Class_02;

var cat1 = new Cat("Whiskers", 3);
var cat2 = new Cat("Mittens", 2);
var cat3 = new Cat("Shadow", 4);

Console.WriteLine(Cat.Counter);//靜態成員,因此用類別名稱存取
Console.WriteLine(Cat.DoCount());//靜態成員,因此用類別名稱存取
Console.WriteLine(cat1.CatCount());//非靜態成員,因此用物件名稱存取

靜態類別(Static Classes)

靜態類別(static class)是指 整個類別只能用來存放「共用的功能或資料」,不能被建立成物件(不能new) 的類別,也就是說它本身就代表一組工具功能集合,裡面的所有成員都必須是static,只能透過「類別名稱」直接使用,例如Mathf或Console,常用來做工具函式計算方法全域輔助功能,因為它不需要保存每個物件的狀態,所以更適合放「不依賴個體」的邏輯。

namespace Class_02;

public static class AutoFeeder
{
    private static Random Rnd = new Random();
    private static string[] _foods = new string[] { "kibble", "canned food", "treats", "raw food" };

    public static string MakeFood()
    {
        int index = Rnd.Next(_foods.Length);//0, 1, 2, 3
        return _foods[index];
    }
}
using Class_02;

var dog =  new Dog("kevin", 5);
var cat =  new Cat("Whiskers", 3);
var count = 0;

while (true)
{
    var food = AutoFeeder.MakeFood();

    switch(food)
    {
        case"kibble":
            Console.WriteLine($"{dog.Name} is eating {food}");
            count++;
            break;
        case "canned food":
            Console.WriteLine($"{cat.Name} is eating {food}");
            count++;
            break;
        case "treats":
            Console.WriteLine($"{dog.Name} and {cat.Name} are sharing {food}");
            count++;
            break;
        case "raw food":
            Console.WriteLine($"{dog.Name} and {cat.Name} are fighting over {food}");
            count++;
            break;
        default: Console.WriteLine("Unknown food");
            count++;
            break;

    }

    Console.WriteLine("Press enter to continue...");
    Console.ReadLine();
}

這篇筆記把多載預設參數運算子多載,還有靜態成員靜態類別都紀錄一遍。簡單來說,多載預設參數能讓函式呼叫更靈活;運算子多載讓自訂物件也能直接用加減乘除,寫起來超直覺;而靜態(static)系列就是拿來搞定那些不用new、大家共用的工具和資料。學會這些技巧並靈活運用,程式碼不只變得更乾淨,架構也會變得更好維護。

下一篇將介紹get/set/init的使用方式。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言