首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 泛型程式設計完整指南:從基礎到進階模式

     編輯:關於C#

前言

泛型是 C# 2.0 引入的核心特性,提供型別安全、效能與重用性的三重保障。本文系統涵蓋泛型類別、方法、介面、委派、約束、協變/逆變、進階模式與最佳實踐。

基礎語法


// 泛型類別
public class Stack
{
    private T[] _items;
    private int _count;
    
    public Stack(int capacity = 10)
    {
        _items = new T[capacity];
    }
    
    public void Push(T item)
    {
        if (_count == _items.Length) Resize();
        _items[_count++] = item;
    }
    
    public T Pop()
    {
        if (_count == 0) throw new InvalidOperationException("Stack empty");
        return _items[--_count];
    }
    
    public T Peek() => _count > 0 ? _items[_count - 1] : default;
    public int Count => _count;
    
    private void Resize()
    {
        Array.Resize(ref _items, _items.Length * 2);
    }
}

// 使用
var intStack = new Stack();
intStack.Push(1);
intStack.Push(2);
int value = intStack.Pop(); // 型別安全,無裝箱

泛型方法


public static class Utility
{
    // 交換兩個變數
    public static void Swap(ref T a, ref T b)
    {
        T temp = a;
        a = b;
        b = temp;
    }
    
    // 取得陣列最大值
    public static T Max(T[] array) where T : IComparable
    {
        if (array.Length == 0) throw new ArgumentException("Empty array");
        T max = array[0];
        for (int i = 1; i < array.Length; i++)
            if (array[i].CompareTo(max) > 0) max = array[i];
        return max;
    }
    
    // 建立預設值字典
    public static Dictionary CreateDictionary()
        where TKey : notnull
    {
        return new Dictionary();
    }
}

型別約束

約束語法說明範例
實值型別where T : struct不可為 nullint, double, DateTime, 自訂 struct
參考型別where T : class可為 nullstring, 自訂 class, 介面, 委派
建構函式where T : new()需有無參數建構函式new T()
基底類別where T : BaseClass繼承關係Entity, Control
介面where T : IInterface實作介面IComparable<T>, IDisposable
非受控型別where T : unmanaged無參考欄位int, bool, 自訂 struct (無 ref 欄位)
可為 null 實值型別where T : struct?C# 9+int?, DateTime?
預設建構函式 + 實值型別where T : struct, new()組合約束所有 struct 隱含有 new()

多型別參數


// 字典鍵值對
public class Pair
{
    public TKey Key { get; set; }
    public TValue Value { get; set; }
    
    public Pair(TKey key, TValue value)
    {
        Key = key;
        Value = value;
    }
    
    public void Deconstruct(out TKey key, out TValue value)
    {
        key = Key;
        value = Value;
    }
}

// 使用
var pair = new Pair("Age", 25);
var (key, value) = pair; // 解構

泛型介面與協變/逆變


// 協變:out T (只作為輸出/回傳)
public interface IEnumerable : IEnumerable
{
    IEnumerator GetEnumerator();
}

// 逆變:in T (只作為輸入/參數)
public interface IComparer
{
    int Compare(T x, T y);
}

// 使用協變
IEnumerable strings = new List();
IEnumerable objects = strings; // 合法:string → object

// 使用逆變
IComparer objComparer = (x, y) => 0;
IComparer strComparer = objComparer; // 合法:object ← string


進階模式

1. Curiously Recurring Template Pattern (CRTP)


public abstract class Entity where T : Entity
{
    public int Id { get; protected set; }
    
    public bool Equals(T other) => other != null && Id == other.Id;
    public override bool Equals(object obj) => obj is T other && Equals(other);
    public override int GetHashCode() => Id.GetHashCode();
    
    public static bool operator ==(Entity a, Entity b)
    {
        if (ReferenceEquals(a, b)) return true;
        if (a is null || b is null) return false;
        return a.Equals(b);
    }
    
    public static bool operator !=(Entity a, Entity b) => !(a == b);
}

public class User : Entity
{
    public string Name { get; set; }
}

public class Product : Entity
{
    public string SKU { get; set; }
}

2. 型別安全建構者模式


public class PersonBuilder where T : PersonBuilder
{
    protected string _name;
    protected int _age;
    
    public T WithName(string name)
    {
        _name = name;
        return (T)this;
    }
    
    public T WithAge(int age)
    {
        _age = age;
        return (T)this;
    }
    
    public Person Build() => new Person(_name, _age);
}

public class EmployeeBuilder : PersonBuilder
{
    public EmployeeBuilder WithCompany(string company)
    {
        // 擴充屬性
        return this;
    }
}

3. 策略模式泛型化


public interface ISerializer
{
    string Serialize(T obj);
    T Deserialize(string data);
}

public class JsonSerializer : ISerializer
{
    public string Serialize(T obj) => System.Text.Json.JsonSerializer.Serialize(obj);
    public T Deserialize(string data) => System.Text.Json.JsonSerializer.Deserialize(data);
}

public class XmlSerializer : ISerializer
{
    public string Serialize(T obj) 
    {
        var xs = new System.Xml.Serialization.XmlSerializer(typeof(T));
        using var sw = new StringWriter();
        xs.Serialize(sw, obj);
        return sw.ToString();
    }
    
    public T Deserialize(string data)
    {
        var xs = new System.Xml.Serialization.XmlSerializer(typeof(T));
        using var sr = new StringReader(data);
        return (T)xs.Deserialize(sr);
    }
}

// 使用
ISerializer serializer = new JsonSerializer();
string json = serializer.Serialize(new Person("John", 30));

4. 泛型宿主服務


public interface IRepository where T : class
{
    T GetById(int id);
    IEnumerable GetAll();
    void Add(T entity);
    void Update(T entity);
    void Delete(T entity);
}

public class Repository : IRepository where T : class
{
    private readonly DbContext _context;
    
    public Repository(DbContext context) => _context = context;
    
    public T GetById(int id) => _context.Set().Find(id);
    public IEnumerable GetAll() => _context.Set().ToList();
    public void Add(T entity) => _context.Set().Add(entity);
    public void Update(T entity) => _context.Set().Update(entity);
    public void Delete(T entity) => _context.Set().Remove(entity);
}

// 註冊 (DI 容器)
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));

// 使用
public class UserService
{
    private readonly IRepository _userRepo;
    private readonly IRepository _orderRepo;
    
    public UserService(IRepository userRepo, IRepository orderRepo)
    {
        _userRepo = userRepo;
        _orderRepo = orderRepo;
    }
}

效能考量

面向影響建議
程式碼膨脹每個具體型別產生獨立程式碼避免過度泛型化,熱點路徑可接受
實值型別每個 struct 產生專屬程式碼優點:無裝箱;缺點:程式碼重複
參考型別共享同一組程式碼 (型別抹除)記憶體效率佳
JIT 編譯首次使用時編譯預熱關鍵路徑

常見陷阱


// 1. 靜態欄位每個封閉型別獨立
public class Counter
{
    public static int Count = 0; // Counter.Count ≠ Counter.Count
}

// 2. 遞迴泛型定義
public class Node 
{
    public T Value;
    public Node Next; // 正確
    // public Node> Next; // 錯誤:無限遞迴
}

// 3. 預設值陷阱
public T GetDefault() => default; // struct 回傳 0/false,class 回傳 null

// 4. 運算子限制
public T Add(T a, T b) // 錯誤:T 不支援 +
{
    return a + b; // 需約束:where T : IAdditionOperators (C# 11+)
}

現代 C# 泛型新特性


// C# 11: 靜態虛擬介面成員 (數學運算)
public T Sum(IEnumerable values) where T : INumber
{
    T sum = T.Zero;
    foreach (var v in values) sum += v;
    return sum;
}

// C# 11: required 修飾詞
public class Person
{
    public required string Name { get; init; }
    public required int Age { get; init; }
}

// C# 12: 預設 lambda 參數
var factory = () => new List(); // 隱含型別

// .NET 7: System.Numerics 泛型數學
public T Calculate(T a, T b) where T : INumber
{
    return T.Max(a, b) * T.One;
}

總結

泛型提供 型別安全 (編譯期檢查)、效能 (實值型別無裝箱)、重用性 (一份程式碼服務多型別) 三大核心價值。掌握約束條件、協變/逆變、CRTP、策略模式泛型化等進階模式,能設計出優雅且高效的 API。現代 C# 持續強化泛型能力 (靜態虛擬介面、泛型數學、required 修飾詞),使泛型成為類別庫與框架設計的基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved