C# 泛型与集合

2 minIntermediate2026/6/14

泛型类与方法、约束、协变与逆变、List/Dictionary/HashSet/Queue/Stack、LINQ to Objects、迭代器(yield)

1. 泛型基础

1.1 泛型与方法

// 泛型类
public class Repository<T> where T : class
{
    private readonly List<T> _items = [];

    public void Add(T item) => _items.Add(item);

    public T? FindById(int id) => _items.FirstOrDefault();

    public IReadOnlyList<T> GetAll() => _items.AsReadOnly();

    public int Count => _items.Count;
}

// 泛型方法
public static class Algorithms
{
    // 交换两个值
    public static void Swap<T>(ref T a, ref T b)
    {
        (a, b) = (b, a);
    }

    // 泛型方法带约束
    public static T Max<T>(T a, T b) where T : IComparable<T>
    {
        return a.CompareTo(b) >= 0 ? a : b;
    }

    // 多类型参数
    public static TResult Convert<TInput, TResult>(
        TInput input, Func<TInput, TResult> converter)
    {
        return converter(input);
    }
}

// 使用
var repo = new Repository<Product>();
repo.Add(new Product { Name = "商品A", Price = 99.9m });

int x = 3, y = 5;
Algorithms.Swap(ref x, ref y); // x=5, y=3

var max = Algorithms.Max(42, 17); // 42
var result = Algorithms.Convert("123", int.Parse); // 123

1.2 泛型约束

// where T : struct          - 值类型
// where T : class           - 引用类型
// where T : class?          - 可空引用类型
// where T : default         - 无约束(C# 9+,用于消除歧义)
// where T : new()           - 有无参公共构造函数
// where T : BaseClass       - 继承自 BaseClass
// where T : IInterface      - 实现 IInterface
// where T : unmanaged       - 非托管类型
// where T : notnull         - 非空值类型或非空引用类型
// where T : Enum            - 枚举类型
// where T : Delegate        - 委托类型

public class Factory<T> where T : new()
{
    public T Create() => new T();
}

public class NumberProcessor<T> where T : struct, IComparable<T>
{
    public T FindMax(T[] array)
    {
        T max = array[0];
        foreach (var item in array)
        {
            if (item.CompareTo(max) > 0)
                max = item;
        }
        return max;
    }
}

// 多约束
public class Service<TInput, TOutput>
    where TInput : class, ICloneable
    where TOutput : new()
{
    public TOutput Process(TInput input)
    {
        var clone = input.Clone();
        return new TOutput();
    }
}

2. 协变逆变

2.1 概念理解

协变 (Covariant)     → 类型方向一致(子→父)  → out 修饰符
逆变 (Contravariant) → 类型方向相反(父→子)  → in  修饰符

2.2 协变 (out)

// 协变:输出位置使用,允许子类型→父类型转换
public interface IEnumerable<out T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}

// 因为 out,可以做如下转换
IEnumerable<string> strings = new List<string> { "a", "b" };
IEnumerable<object> objects = strings; // 合法!string → object

// 自定义协变接口
public interface IProducer<out T>
{
    T Produce();
    // void Consume(T item); // 编译错误!out 参数只能用于输出位置
}

public class StringProducer : IProducer<string>
{
    public string Produce() => "Hello";
}

IProducer<object> producer = new StringProducer(); // 合法
object result = producer.Produce();

2.3 逆变 (in)

// 逆变:输入位置使用,允许父类型→子类型转换
public interface IComparer<in T>
{
    int Compare(T? x, T? y);
}

// 因为 in,可以做如下转换
IComparer<object> objectComparer = Comparer<object>.Default;
IComparer<string> stringComparer = objectComparer; // 合法!

// 自定义逆变接口
public interface IConsumer<in T>
{
    void Consume(T item);
    // T Produce(); // 编译错误!in 参数只能用于输入位置
}

public class ObjectConsumer : IConsumer<object>
{
    public void Consume(object item) => Console.WriteLine(item);
}

IConsumer<string> consumer = new ObjectConsumer(); // 合法
consumer.Consume("Hello");

3. 集合

3.1 List<T>

var list = new List<int> { 1, 2, 3, 4, 5 };

// 添加与插入
list.Add(6);
list.AddRange([7, 8, 9]);
list.Insert(0, 0); // 在索引0处插入

// 访问
int first = list[0];
int last = list[^1];

// 查找
bool has = list.Contains(3);
int index = list.IndexOf(3);
int found = list.Find(x => x > 5);         // 第一个匹配
List<int> allFound = list.FindAll(x => x > 3);

// 删除
list.Remove(3);           // 删除第一个3
list.RemoveAt(0);         // 删除索引0
list.RemoveAll(x => x > 5); // 删除所有>5的
list.RemoveRange(0, 2);   // 删除范围

// 排序
list.Sort();                           // 默认排序
list.Sort((a, b) => b.CompareTo(a));   // 降序

// 遍历
list.ForEach(item => Console.WriteLine(item));

// 容量
list.TrimExcess(); // 释放多余容量
Console.WriteLine($"Count: {list.Count}, Capacity: {list.Capacity}");

3.2 Dictionary<TKey, TValue>

var dict = new Dictionary<string, int>
{
    ["apple"] = 5,
    ["banana"] = 3,
    ["orange"] = 7
};

// 添加
dict.Add("grape", 4);           // 键不存在时添加
dict["mango"] = 6;              // 添加或更新
dict.TryAdd("pear", 2);         // 尝试添加

// 访问
int count = dict["apple"];      // 键不存在抛异常
if (dict.TryGetValue("banana", out int value))
{
    Console.WriteLine($"banana: {value}");
}

// 安全访问(C# 8+)
int safe = dict.GetValueOrDefault("kiwi", 0);

// 遍历
foreach (var (key, val) in dict)
{
    Console.WriteLine($"{key}: {val}");
}

// 检查
bool hasKey = dict.ContainsKey("apple");
bool hasValue = dict.ContainsValue(5);

// 删除
dict.Remove("apple");
dict.Remove("banana", out int removed); // 删除并获取值

// .NET 8+ GetOrAdd / AddOrUpdate(ConcurrentDictionary)
var concurrent = new ConcurrentDictionary<string, int>();
concurrent.TryAdd("key", 1);
concurrent.AddOrUpdate("key", _ => 1, (_, old) => old + 1);

3.3 HashSet<T> 与其他集合

// HashSet - 高性能集合操作
var set1 = new HashSet<int> { 1, 2, 3, 4, 5 };
var set2 = new HashSet<int> { 4, 5, 6, 7, 8 };

set1.IntersectWith(set2);        // 交集: {4, 5}
set1.UnionWith(set2);            // 并集: {1,2,3,4,5,6,7,8}
set1.ExceptWith(set2);           // 差集: {1,2,3}
set1.SymmetricExceptWith(set2);  // 对称差集

bool isSubset = set1.IsSubsetOf(set2);
bool isSuperset = set1.IsSupersetOf(set2);

// Queue<T> - 先进先出
var queue = new Queue<string>();
queue.Enqueue("第一个");
queue.Enqueue("第二个");
string dequeued = queue.Dequeue(); // "第一个"
string peeked = queue.Peek();      // "第二个"

// Stack<T> - 后进先出
var stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
stack.Push(3);
int popped = stack.Pop();  // 3
int top = stack.Peek();    // 2

// PriorityQueue<TElement, TPriority> (.NET 6+)
var pq = new PriorityQueue<string, int>();
pq.Enqueue("低优先级", 3);
pq.Enqueue("高优先级", 1);
pq.Enqueue("中优先级", 2);
string highest = pq.Dequeue(); // "高优先级"

// LinkedList<T> - 双向链表
var linked = new LinkedList<int>();
linked.AddLast(1);
linked.AddLast(2);
linked.AddFirst(0);
LinkedListNode<int>? node = linked.Find(1);
linked.AddBefore(node!, 5);

3.4 不可变集合

using System.Collections.Immutable;

// 不可变列表
var list = ImmutableList.Create(1, 2, 3);
var newList = list.Add(4); // 返回新列表,原列表不变

// 不可变字典
var dict = ImmutableDictionary.CreateBuilder<string, int>()
    .ToImmutable();

// 使用 ImmutableArray(性能更好)
var arr = ImmutableArray.Create(1, 2, 3);
var arr2 = arr.Add(4);

// Frozen 集合(.NET 8+,创建后不可修改,查询极快)
var frozen = new Dictionary<string, int>
{
    ["a"] = 1, ["b"] = 2
}.ToFrozenDictionary();

int val = frozen["a"]; // O(1) 查询,性能优于不可变集合

4. LINQ to Objects

4.1 常用查询操作

var products = new List<Product>
{
    new("笔记本", 5999m, "电子产品"),
    new("手机", 3999m, "电子产品"),
    new("T恤", 199m, "服装"),
    new("牛仔裤", 299m, "服装"),
    new("耳机", 899m, "电子产品"),
};

// 筛选
var expensive = products.Where(p => p.Price > 1000);
var electronics = products.Where(p => p.Category == "电子产品");

// 投影
var names = products.Select(p => p.Name);
var projections = products.Select(p => new { p.Name, p.Price });

// 排序
var sorted = products.OrderBy(p => p.Price);
var sortedDesc = products.OrderByDescending(p => p.Price);
var multiSort = products.OrderBy(p => p.Category).ThenBy(p => p.Price);

// 分组
var grouped = products.GroupBy(p => p.Category);
foreach (var group in grouped)
{
    Console.WriteLine($"{group.Key}: {string.Join(", ", group.Select(p => p.Name))}");
}

// 聚合
decimal total = products.Sum(p => p.Price);
decimal avg = products.Average(p => p.Price);
decimal max = products.Max(p => p.Price);
int count = products.Count(p => p.Price > 500);

// 分页
var page = products.OrderBy(p => p.Price).Skip(10).Take(5);

// 去重
var categories = products.Select(p => p.Category).Distinct();

// 集合操作
var allNames = new[] { "A", "B", "C" };
var someNames = new[] { "B", "C", "D" };
var intersect = allNames.Intersect(someNames);     // {B, C}
var union = allNames.Union(someNames);              // {A, B, C, D}
var except = allNames.Except(someNames);            // {A}

4.2 查询语法 vs 方法语法

// 查询语法
var query = from p in products
            where p.Price > 500
            orderby p.Price descending
            select new { p.Name, p.Price };

// 方法语法(等价,更常用)
var method = products
    .Where(p => p.Price > 500)
    .OrderByDescending(p => p.Price)
    .Select(p => new { p.Name, p.Price });

// 复杂查询 - Join
var orders = new List<Order> { /* ... */ };
var result = from p in products
             join o in orders on p.Name equals o.ProductName
             select new { p.Name, o.Quantity, Total = p.Price * o.Quantity };

5. 迭代器 (yield)

5.1 yield return

// 生成斐波那契数列
public static IEnumerable<int> Fibonacci(int count)
{
    int a = 0, b = 1;
    for (int i = 0; i < count; i++)
    {
        yield return a;
        (a, b) = (b, a + b);
    }
}

// 使用
foreach (var num in Fibonacci(10))
{
    Console.WriteLine(num); // 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
}

// 惰性读取文件行
public static IEnumerable<string> ReadLinesLazy(string path)
{
    using var reader = new StreamReader(path);
    while (reader.ReadLine() is string line)
    {
        yield return line;
    }
}

// yield break 提前退出
public static IEnumerable<int> GetPositive(int[] numbers)
{
    foreach (var n in numbers)
    {
        if (n < 0)
            yield break; // 遇到负数停止
        if (n > 0)
            yield return n;
    }
}

5.2 迭代器组合

// 链式迭代器 - 惰性求值
public static IEnumerable<T> Filter<T>(
    this IEnumerable<T> source, Func<T, bool> predicate)
{
    foreach (var item in source)
    {
        if (predicate(item))
            yield return item;
    }
}

public static IEnumerable<TResult> Map<TSource, TResult>(
    this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
    foreach (var item in source)
    {
        yield return selector(item);
    }
}

// 组合使用 - 全部惰性执行
var result = Fibonacci(100)
    .Filter(n => n % 2 == 0)
    .Map(n => n * n)
    .Take(5);

6. 集合表达式 (C# 12)

// 统一的集合初始化语法
int[] array = [1, 2, 3];
List<string> list = ["a", "b", "c"];
Span<int> span = [1, 2, 3];
HashSet<string> set = ["x", "y", "z"];

// 展开运算符 (..)
int[] a = [1, 2, 3];
int[] b = [4, 5, 6];
int[] combined = [..a, ..b, 7, 8]; // [1,2,3,4,5,6,7,8]

// 方法参数
void Process(ReadOnlySpan<int> values) { }
Process([1, 2, 3, 4, 5]);

7. 性能选择指南

需求推荐原因
频繁随机访问List<T> / ArrayO(1) 索引访问
快速查找Dictionary<TKey,TValue>O(1) 键查找
去重HashSet<T>O(1) 包含检查
排序后查找SortedDictionary / SortedSetO(log n) 有序操作
先进先出Queue<T>语义清晰
优先级处理PriorityQueue<T,E>O(log n) 出队
不可变数据ImmutableArray<T>线程安全
只读快查FrozenDictionary (.NET 8+)极速只读查询
小型值集合readonly record struct栈分配,无GC