掌握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確保只有整數可以新增到數字列表中,只有字串可以新增到字串列表中。
型別安全與效率
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.1TypeChecker(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
}這些約束有助於確保泛型型別滿足必要的標準,防止運行時錯誤並增強型別安全。
Microsoft對INumber的實施
Tim討論了Microsoft如何使用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類別,演示了如何將泛型與不同的數值型別(如雙精度和小數)一起使用。
Tim展示如何為整數和雙精度數建立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.8MathOperations<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強調型別安全的重要性,指出您不能混合不同的數值型別。例如,嘗試將雙精度數加至整數會導致編譯時錯誤。
// 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解釋了使用泛型以避免型別轉換相關的開銷的好處。 例如,將整數轉換為雙精度以進行數學運算,然後再轉回整數可能是耗費資源的。 使用泛型允許直接在本機型別上進行操作,保持性能和精度。
泛型在實踐中的應用
Tim建議在使用泛型時要謹慎,建議開發者適當使用它們並避免過度使用。 他強調了泛型的優勢,如型別安全、減少裝箱和拆箱、編譯時檢查以及增強程式碼可讀性。
他還指出,泛型在集合(如List和Dictionary<TKey, TValue>)中很普遍,以及在日誌框架中,這些框架可以在不知道具體細節的情況下處理各種型別。
結論
Tim Corey對C#中高級泛型的深入探索為它們的實際應用和優勢提供了寶貴的見解。 如果您想深入了解泛型並查看實際例子,務必觀看Tim Corey的詳細影片C#泛型。 他的清晰解釋和實際操作演示將幫助您完全掌握這些概念,並在自己的項目中有效應用它們。

