iT邦幫忙

2025 iThome 鐵人賽

DAY 7
0
佛心分享-IT 人自學之術

30 天從 Python 轉職場 C# 新手入門系列 第 7

Day7-C# 的函式(方法)與參數-2

  • 分享至 

  • xImage
  •  

前言

由於昨天的進度並沒有完全完成,所以今天要接續昨日所學的 Method ,昨天的進度到了 Method invocation(調用) ,學到了不同類型的函示他們之間不同的呼叫方式。那麼今天會繼續在這份參考資料的內容做學習,事不宜遲趕快開始吧!


繼承與覆寫方法 (Inherited and Overridden Methods)

在 C# 中,一個類別除了自己定義的成員,也會繼承基底類別的成員。所有類別都直接或間接繼承自 Object 類別,因此會自動擁有:Equals(Object)、GetType()、ToString()。

透過下方程式碼與之結果可以看到,Equals 方法並沒有在 Person 類別裡定義,而是繼承自 Object,但預設的 Equals 是比較記憶體參考(物件是否同一個),所以結果是 False。

public class Person
{
    public string FirstName = default!;
}

public static class ClassTypeExample
{
    public static void Main()
    {
        Person p1 = new() { FirstName = "John" };
        Person p2 = new() { FirstName = "John" };
        Console.WriteLine($"p1 = p2: {p1.Equals(p2)}");
    }
}
// 輸出:p1 = p2: False

輸出結果:p1 = p2: False

若今天要覆寫 Methods ,要改變繼承方法的行為,可以使用 override 關鍵字覆寫。覆寫時,方法簽章 (signature) 必須一致。通常在覆寫 Equals 時,也需要覆寫 GetHashCode,以確保一致性。可以透過下方例子與結果跟上方例子做比較,他將 Equals override 之後,改成比較 FirstName 是否相同。因此兩個 Person 物件的 FirstName 都是 "John",所以輸出 True。

namespace methods;

public class Person
{
    public string FirstName = default!;

    public override bool Equals(object? obj) =>
        obj is Person p2 &&
        FirstName.Equals(p2.FirstName);

    public override int GetHashCode() => FirstName.GetHashCode();
}

public static class Example
{
    public static void Main()
    {
        Person p1 = new() { FirstName = "John" };
        Person p2 = new() { FirstName = "John" };
        Console.WriteLine($"p1 = p2: {p1.Equals(p2)}");
    }
}
// 輸出:p1 = p2: True

經過上方範例的比較可以做個簡單的重點整理:

  1. 所有類別 都繼承自 Object,因此自動擁有一些基本方法。Object class可以參考這裡
  2. 預設的 Equals 比較的是物件的記憶體位址,而不是內容。
  3. 使用 override 可以改寫繼承的方法。
  4. 覆寫 Equals 時,建議也覆寫 GetHashCode,保持一致性。
  5. 覆寫方法時,方法簽章必須與被覆寫的方法相同。

參數傳遞(Passing parameters)

在 C# 中,型別分為 值型別 (Value Type) 和 參考型別 (Reference Type)。不論是哪一種,預設都是 傳值 (Pass by Value) 傳遞到方法中。

傳值 (Pass by Value)

  1. Value Type傳值
    當把值型別 (如 int, double, struct) 傳遞給方法時,會傳遞「值的複本」,Method內對參數的修改,不會影響到原本變數。
public static class ByValueExample
{
    public static void Main()
    {
        var value = 20;
        Console.WriteLine("In Main, value = {0}", value);
        ModifyValue(value);
        Console.WriteLine("Back in Main, value = {0}", value);
    }

    static void ModifyValue(int i)
    {
        i = 30;
        Console.WriteLine("In ModifyValue, parameter value = {0}", i);
    }
}

Output->
In Main, value = 20
In ModifyValue, parameter value = 30
Back in Main, value = 20
2. Reference Type傳值
當把參考型別 (如 class, array, string) 傳遞給方法時,實際傳遞的是「物件位址的複本」。Method內修改物件的內容,會影響到原始物件。但如果在方法內 改變參數的整個物件參考,則不會影響外部。

public class SampleRefType
{
    public int value;
}

public static class ByRefTypeExample
{
    public static void Main()
    {
        var rt = new SampleRefType { value = 44 };
        ModifyObject(rt);
        Console.WriteLine(rt.value);
    }

    static void ModifyObject(SampleRefType obj) => obj.value = 33;
}

Output->33
➡️ 修改了物件內部的 value,呼叫端也被改變。

傳參考 (Pass by Reference)

使用 ref、out、in 關鍵字,可以讓方法直接操作變數的記憶體位置,而不是複本。

  1. ref:允許方法修改呼叫端的變數,必須在呼叫端初始化變數。
public static class ByRefExample
{
    public static void Main()
    {
        var value = 20;
        Console.WriteLine("In Main, value = {0}", value);
        ModifyValue(ref value);
        Console.WriteLine("Back in Main, value = {0}", value);
    }

    private static void ModifyValue(ref int i)
    {
        i = 30;
        Console.WriteLine("In ModifyValue, parameter value = {0}", i);
    }
}

Output->
In Main, value = 20
In ModifyValue, parameter value = 30
Back in Main, value = 30
➡️ 可以看到跟上方Pass by Value的第一個範例不同,方法修改了變數,呼叫端的值也跟著改。
2. ref 的應用:交換值

public static class RefSwapExample
{
    static void Main()
    {
        int i = 2, j = 3;
        Console.WriteLine($"i = {i}  j = {j}");

        Swap(ref i, ref j);

        Console.WriteLine($"i = {i}  j = {j}");
    }

    static void Swap(ref int x, ref int y) =>
        (y, x) = (x, y);
}

Output->
i = 2 j = 3
i = 3 j = 2
➡️ 使用 ref 可直接交換兩個變數的值。
3. out:必須在方法內賦值,用來從方法回傳多個值。
4. in:傳遞參數時避免複製,但保護參數不被修改。


Parameter collections(params)

在 C# 中,方法呼叫時通常需要指定「確切數量」的參數,但有時候這樣太受限制,例如想要傳入 可變數量的引數。params 關鍵字允許你在方法參數中指定一個「參數集合 (parameter collection)」,讓方法可以被呼叫時傳入:一個集合、一串逗號分隔的值、null、或什麼都不傳。params 參數必須是 集合型別 (如陣列、IEnumerable<T>),params 參數必須是方法參數列表的最後一個參數。可以看一下以下範例:

static class ParamsExample
{
    static void Main()
    {
        string fromArray = GetVowels(["apple", "banana", "pear"]);
        Console.WriteLine($"Vowels from collection expression: '{fromArray}'");

        string fromMultipleArguments = GetVowels("apple", "banana", "pear");
        Console.WriteLine($"Vowels from multiple arguments: '{fromMultipleArguments}'");

        string fromNull = GetVowels(null);
        Console.WriteLine($"Vowels from null: '{fromNull}'");

        string fromNoValue = GetVowels();
        Console.WriteLine($"Vowels from no value: '{fromNoValue}'");
    }

    static string GetVowels(params IEnumerable<string>? input)
    {
        if (input == null || !input.Any())
        {
            return string.Empty;
        }

        char[] vowels = ['A', 'E', 'I', 'O', 'U'];
        return string.Concat(
            input.SelectMany(
                word => word.Where(letter => vowels.Contains(char.ToUpper(letter)))));
    }
}

Output->
Vowels from collection expression: 'aeaaaea'
Vowels from multiple arguments: 'aeaaaea'
Vowels from null: ''
Vowels from no value: ''
可以看到就如同上面所說的params可以傳入一個集合、一串逗號分隔的值、null、或什麼都不傳,在Main裡面分別呼叫了這四種形式,而結果也能看到每一個的結果。


上一篇
Day6-C# 的函式(方法)與參數
下一篇
Day8-Python vs C# 比較
系列文
30 天從 Python 轉職場 C# 新手入門12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言