掌握 C# 泛型
C#中的泛型自引入以來就成為語言的重要組成部分,即使不是所有開發者都完全了解其運作,仍然提供了許多好處。 在他的影片中,"如何在C#中創建泛型,包括新功能", Tim Corey解釋了為何泛型重要,如何創建它們,並展示其實際應用。
本文提供了C#泛型的全面指南,並提供Tim Corey的相關影片中有價值的見解。 本文涵蓋了泛型的基礎知識,包括類型安全、性能優勢和實際應用。 本文還探討了如何創建泛型方法、類別、接口,以及如何對泛型施加限制。 此外,本文還強調了真實世界的使用案例,以及使用泛型撰寫高效、類型安全代碼的重要性。
介紹
C#泛型提供了一種強大的方式,讓開發者能夠定義類別、方法、接口和集合,這些可與任何數據類型配合,從而創建靈活、可重用且類型安全的代碼。 通過使用泛型類或泛型方法,開發者可以定義泛型類型參數(例如,T),可代表任何數據類型。 這樣可以消除代碼重複,增強代碼重用,同時確保在編譯時的類型安全。泛型集合類,如List和Dictionary<TKey, TValue>,允許高效處理不同的數據類型,而泛型接口和泛型委託則能創建可與多個類型參數配合的自訂泛型類型。 通過利用泛型,開發者可以最大化代碼效率並最小化非泛型類別的缺點。 這種靈活性允許創建更可重用的代碼,而不必犧牲性能或類型安全。
Tim在(0:00)強調了泛型在C#中廣泛使用的重要性。 他的目的是解釋為何泛型至關重要,並展示如何有效地創建和使用它們。
創建專案
Tim首先創建了一個名為"GenericsDemoApp"的新控制台應用程式,專注於演示泛型而無需任何UI干擾。 他使用.NET 8和Visual Studio 2022設置了此專案。
泛型的基礎
Tim在(2:22)用List類作為例子來說明泛型的概念。 泛型允許指定集合可以容納的元素類型,提供類型安全並避免運行時錯誤。
List<int> numbers = new List<int> { 1, 2, 3 };
List<string> strings = new List<string> { "Tim", "Corey", "Sue" };
List<int> numbers = new List<int> { 1, 2, 3 };
List<string> strings = new List<string> { "Tim", "Corey", "Sue" };
List確保只能將整數加入numbers列表,字符串只能加入strings列表。
類型安全和效能
Tim強調泛型提供的類型安全的重要性。 編譯器在設計時檢查類型,防止類型不匹配並確保安全的代碼執行。 泛型還避免了裝箱和開箱的需要,從而提高了代碼效率。
非泛型集合的效率低下
為了展示使用非泛型集合的效率低下,Tim創建了一個List並展示如何容納不同類型的對象。
List<object> objects = new List<object> { "Tim", 4, 3.6m };
List<object> objects = new List<object> { "Tim", 4, 3.6m };
他解釋說,使用非泛型集合可能因為裝箱和開箱而導致類型不匹配和效率低下。
性能比較
Tim在(6:15)運行性能比較,分別使用List
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 1; i <= 1_000_000; i++)
{
objects.Add(i);
}
stopwatch.Stop();
Console.WriteLine($"List of object elapsed time: {stopwatch.ElapsedMilliseconds} ms");
stopwatch.Restart();
for (int i = 1; i <= 1_000_000; i++)
{
numbers.Add(i);
}
stopwatch.Stop();
Console.WriteLine($"List of int elapsed time: {stopwatch.ElapsedMilliseconds} ms");
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 1; i <= 1_000_000; i++)
{
objects.Add(i);
}
stopwatch.Stop();
Console.WriteLine($"List of object elapsed time: {stopwatch.ElapsedMilliseconds} ms");
stopwatch.Restart();
for (int i = 1; i <= 1_000_000; i++)
{
numbers.Add(i);
}
stopwatch.Stop();
Console.WriteLine($"List of int elapsed time: {stopwatch.ElapsedMilliseconds} ms");
結果顯示,因為沒有裝箱和開箱,向List

創建類型檢查器方法
Tim在(10:14)展示如何創建一個名為TypeChecker的泛型方法。 此方法檢查給定值的類型並將其打印出來,說明了泛型的靈活性和力量。
public static void TypeChecker<t>(T value)
{
Console.WriteLine($"Type: {typeof(T)}, Value: {value}");
}
public static void TypeChecker<t>(T value)
{
Console.WriteLine($"Type: {typeof(T)}, Value: {value}");
}
TypeChecker方法使用typeof運算子來確定泛型參數T的類型,並打印類型和值。
使用類型檢查器方法
Tim提供了使用不同類型參數調用TypeChecker方法的範例。
TypeChecker(1); // Type: System.Int32, Value: 1
TypeChecker("Tim"); // Type: System.String, Value: Tim
TypeChecker(1.1); // Type: System.Double, Value: 1.1
TypeChecker(1); // Type: System.Int32, Value: 1
TypeChecker("Tim"); // Type: System.String, Value: Tim
TypeChecker(1.1); // Type: System.Double, Value: 1.1
透過傳遞多種類型給TypeChecker方法,Tim展示了泛型如何無縫處理不同數據類型。
創建泛型類別:Better List
Tim在(16:25)開始創建一個名為BetterList的泛型類別。此類別封裝了一個指定類型的列表,並提供額外的功能。
public class BetterList<t>
{
private List<t> data = new List<t>();
public void AddToList(T value)
{
data.Add(value);
Console.WriteLine($"{value} has been added to the list");
}
}
public class BetterList<t>
{
private List<t> data = new List<t>();
public void AddToList(T value)
{
data.Add(value);
Console.WriteLine($"{value} has been added to the list");
}
}
BetterList類別包含一個私有List和一個AddToList方法,該方法將值加入列表並打印一則訊息來指示已添加。
使用Better List類別
Tim提供了使用BetterList類別的不同類型範例。
BetterList<int> betterNumbers = new BetterList<int>();
betterNumbers.AddToList(5);
BetterList<PersonRecord> people = new BetterList<PersonRecord>();
people.AddToList(new PersonRecord("Tim", "Corey"));
BetterList<int> betterNumbers = new BetterList<int>();
betterNumbers.AddToList(5);
BetterList<PersonRecord> people = new BetterList<PersonRecord>();
people.AddToList(new PersonRecord("Tim", "Corey"));
在這些範例中,BetterList
創建泛型接口
Tim在(21:48)介紹了一個名為IImportance的泛型接口的概念。 此接口定義了一個方法以決定哪兩個值更重要。
public interface IImportance<t>
{
T MostImportant(T a, T b);
}
public interface IImportance<t>
{
T MostImportant(T a, T b);
}
實作泛型接口
Tim展示了如何為不同的類型實作此接口。他首先從整數實作開始。
public class EvaluateImportance : IImportance<int>
{
public int MostImportant(int a, int b)
{
return a > b ? a : b;
}
}
public class EvaluateImportance : IImportance<int>
{
public int MostImportant(int a, int b)
{
return a > b ? a : b;
}
}
接下來,他用字符串的長度來決定重要性,為字符串提供了接口的實作。
public class EvaluateStringImportance : IImportance<string>
{
public string MostImportant(string a, string b)
{
return a.Length > b.Length ? a : b;
}
}
public class EvaluateStringImportance : IImportance<string>
{
public string MostImportant(string a, string b)
{
return a.Length > b.Length ? a : b;
}
}
這些實作展示了如何以特定邏輯將相同的接口應用於不同類型。
對泛型施加限制
Tim在(25:21)解釋如何對泛型施加限制,以確保其滿足某些條件。 例如,可以限制泛型類型具有空的構造函數或實作特定的接口。
public class SampleClass<t> where T : new()
{
// Class implementation
}
public class SampleClassWithInterface<t> where T : IImportance<t>
{
// Class implementation
}
public class SampleClass<t> where T : new()
{
// Class implementation
}
public class SampleClassWithInterface<t> where T : IImportance<t>
{
// Class implementation
}
這些限制有助於確保泛型類型符合必要標準,防止運行時錯誤並增強類型安全。
微軟對INumber的實作
Tim討論了微軟如何使用INumber接口對數字運算進行限制。 這允許在泛型類型上進行算術運算,如加法和減法。
public class MathOperations<t> where T : INumber<t>
{
public T Add(T x, T y)
{
return x + y;
}
}
public class MathOperations<t> where T : INumber<t>
{
public T Add(T x, T y)
{
return x + y;
}
}
通過將泛型類型T限制於INumber,確保類型支持數字運算。
將泛型應用於不同的數字類型
Tim在(33:55)擴展了MathOperations類,以展示泛型如何用於不同的數字類型,如double和decimal。
Tim展示了如何為整數和double創建MathOperations實例:
MathOperations<int> intMath = new MathOperations<int>();
Console.WriteLine(intMath.Add(1, 4)); // Outputs: 5
MathOperations<double> doubleMath = new MathOperations<double>();
Console.WriteLine(doubleMath.Add(1.5, 4.3)); // Outputs: 5.8
MathOperations<int> intMath = new MathOperations<int>();
Console.WriteLine(intMath.Add(1, 4)); // Outputs: 5
MathOperations<double> doubleMath = new MathOperations<double>();
Console.WriteLine(doubleMath.Add(1.5, 4.3)); // Outputs: 5.8
此例說明了泛型的靈活性,允許在相同類別內無縫處理不同的數字類型。
處理不同的數字類型
Tim強調類型安全的重要性,展示了您不能混合不同的數字類型。例如,嘗試將double加到整數會導致編譯時錯誤。
// This will result in a compile-time error
// Console.WriteLine(intMath.Add(1.5, 4));
// This will result in a compile-time error
// Console.WriteLine(intMath.Add(1.5, 4));
避免類型轉換開銷
Tim解釋使用泛型以避免類型轉換所帶來的開銷的好處。 例如,將整數轉換為double進行數學運算,然後再轉回整數可能相當耗費資源。 使用泛型允許在原生類型上直接操作,保存效能和精度。
泛型在實踐中
Tim建議在使用泛型時要謹慎,並建議開發者適當地使用它們,避免過度使用。 他強調了泛型帶來的好處,如類型安全、減少裝箱和開箱、編譯時檢查,以及增強代碼可讀性。
他還指出,泛型在集合中普遍存在,如List和Dictionary<TKey, TValue>,以及能處理不同類型而無需事先知道具體內容的日誌框架中。
結論
Tim Corey對C#中高級泛型的詳細探索提供了有關其實際應用和效能的有價值見解。 如果您希望加深對泛型的理解並查看實際世界的範例動作,請務必觀看Tim Corey對C# 泛型的詳細影片。 他清晰的解釋和實際操作示範將幫助您充分掌握這些概念並有效地應用到您自己的專案中。
