由於昨天的進度並沒有完全完成,所以今天要接續昨日所學的 Method ,昨天的進度到了 Method invocation(調用) ,學到了不同類型的函示他們之間不同的呼叫方式。那麼今天會繼續在這份參考資料的內容做學習,事不宜遲趕快開始吧!
在 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
經過上方範例的比較可以做個簡單的重點整理:
在 C# 中,型別分為 值型別 (Value Type) 和 參考型別 (Reference Type)。不論是哪一種,預設都是 傳值 (Pass by Value) 傳遞到方法中。
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,呼叫端也被改變。
使用 ref、out、in 關鍵字,可以讓方法直接操作變數的記憶體位置,而不是複本。
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:傳遞參數時避免複製,但保護參數不被修改。
(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
裡面分別呼叫了這四種形式,而結果也能看到每一個的結果。