C# 泛型与集合
泛型类与方法、约束、协变与逆变、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> / Array | O(1) 索引访问 |
| 快速查找 | Dictionary<TKey,TValue> | O(1) 键查找 |
| 去重 | HashSet<T> | O(1) 包含检查 |
| 排序后查找 | SortedDictionary / SortedSet | O(log n) 有序操作 |
| 先进先出 | Queue<T> | 语义清晰 |
| 优先级处理 | PriorityQueue<T,E> | O(log n) 出队 |
| 不可变数据 | ImmutableArray<T> | 线程安全 |
| 只读快查 | FrozenDictionary (.NET 8+) | 极速只读查询 |
| 小型值集合 | readonly record struct | 栈分配,无GC |