C# 高级教程 - 01 高级泛型

泛型是 C# 中最强大的语言特性之一,基础教程中我们已经掌握了泛型的基本用法——泛型类、泛型方法、以及简单的约束。本文将深入探讨高级泛型技术,包括类型参数的协变与逆变、自定义约束的组合、静态成员与泛型、以及运行时泛型的内省机制。

协变与逆变 (out / in)

协变和逆变解决的是泛型接口/委托的类型兼容性问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 协变:out 关键字 —— 只能作为返回值
public interface IProducer<out T>
{
T Produce();
}

// 逆变:in 关键字 —— 只能作为参数
public interface IConsumer<in T>
{
void Consume(T item);
}

class Animal { }
class Dog : Animal { }

IProducer<Dog> dogProducer = null;
IProducer<Animal> animalProducer = dogProducer; // 协变:子类→父类

IConsumer<Animal> animalConsumer = null;
IConsumer<Dog> dogConsumer = animalConsumer; // 逆变:父类→子类

协变让 IEnumerable<T> 可以安全地将 IEnumerable<Dog> 赋值给 IEnumerable<Animal>,而逆变让 IComparer<Animal> 可以被当作 IComparer<Dog> 使用。

高级约束技巧

除了基础教程中的 where T : class/struct/new(),C# 支持更复杂的约束组合:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 多重约束
public class Repository<TEntity, TKey>
where TEntity : class, IEntity<TKey>, new()
where TKey : struct, IEquatable<TKey>
{
public TEntity Create() => new TEntity();
}

// 值类型约束配合枚举/委托
public class EnumParser<T> where T : struct, Enum
{
public static T Parse(string value) => Enum.Parse<T>(value);
}

// 非托管类型约束(C# 7.3+)
public unsafe class Buffer<T> where T : unmanaged
{
public static void Copy(T* source, T* dest, int count)
{
Buffer.MemoryCopy(source, dest, count * sizeof(T), count * sizeof(T));
}
}

默认值关键字

1
2
3
4
5
6
public T GetOrDefault<T>(IEnumerable<T> source, int index)
{
if (index >= 0 && index < source.Count())
return source.ElementAt(index);
return default; // 值类型为 0,引用类型为 null
}

泛型方法中的类型推断

1
2
3
4
5
6
7
8
// 编译器自动推断类型参数
var result = Combine(1, 2); // T 自动推断为 int

static T Combine<T>(T a, T b) where T : INumber<T> => a + b;

// 泛型数学(C# 11 / .NET 7+)
static T Sum<T>(params T[] values) where T : INumber<T>
=> values.Aggregate(T.Zero, (a, b) => a + b);

运行时泛型内省

1
2
3
4
5
6
7
8
9
10
11
12
class MyGenericClass<TKey, TValue> { }

// 运行时获取泛型定义与类型实参
Type openGeneric = typeof(MyGenericClass<,>);
Type closedGeneric = typeof(MyGenericClass<string, int>);

Console.WriteLine(openGeneric.GetGenericArguments().Length); // 2
Console.WriteLine(closedGeneric.GetGenericArguments()[0]); // System.String

// 运行时构造泛型类型
Type runtimeType = openGeneric.MakeGenericType(typeof(int), typeof(double));
object instance = Activator.CreateInstance(runtimeType)!;

静态成员与泛型

每个封闭泛型类型都拥有独立的静态成员副本:

1
2
3
4
5
6
7
8
9
10
class Counter<T>
{
public static int Count { get; set; }
}

Counter<int>.Count = 10;
Counter<string>.Count = 20;

Console.WriteLine(Counter<int>.Count); // 10(独立)
Console.WriteLine(Counter<string>.Count); // 20(独立)

这使得泛型静态字段可以用作类型到值的映射表,是实现泛型缓存模式的基石。

实战:泛型缓存模式

1
2
3
4
5
6
7
8
public static class PropertyCache<T>
{
public static readonly PropertyInfo[] Properties =
typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
}

// 使用:运行时反射缓存,每个 T 只反射一次
var props = PropertyCache<MyClass>.Properties;

本模式广泛应用于 ORM 框架(如 EF Core、Dapper)以及序列化库中,避免重复反射带来的性能开销。


总结:C# 的高级泛型为类型安全带来了极大的灵活性。理解协变/逆变能让你的 API 更符合直觉,善用泛型约束可以编写更健壮的代码,而泛型缓存模式则是高性能框架设计中的经典技巧。熟练掌握这些技术,是进阶 C# 开发者的必修课。

ByteFisher
分享编程技术 · 记录钓鱼乐趣
扫码关注
▸ 扫码关注 ◂
C#高级教程 系列
第 1/20 篇
分享: