Skip to main content

泛型 (Generics)

什么是泛型?

核心概念

泛型允许您在定义类、接口、方法时使用类型参数,从而编写可重用且类型安全的代码。

为什么需要泛型?

// 使用 object 实现通用容器
public class BoxWithoutGenerics
{
private object _value;

public void Set(object value)
{
_value = value;
}

public object Get()
{
return _value;
}
}

// 使用时需要类型转换
var box = new BoxWithoutGenerics();
box.Set(123);
int value = (int)box.Get(); // 需要强制转换

box.Set("hello");
int wrong = (int)box.Get(); // 运行时异常!
问题
  • ❌ 需要装箱/拆箱(性能损失)
  • ❌ 需要类型转换
  • ❌ 没有编译时类型检查
  • ❌ 运行时可能出错

泛型类

// 单个类型参数
public class Repository<T>
{
private readonly List<T> _items = new();

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

public T GetById(int id) => _items[id];

public IEnumerable<T> GetAll() => _items;
}

// 多个类型参数
public class Pair<TFirst, TSecond>
{
public TFirst First { get; set; }
public TSecond Second { get; set; }

public Pair(TFirst first, TSecond second)
{
First = first;
Second = second;
}
}

// 使用
var userRepo = new Repository<User>();
var pair = new Pair<string, int>("Age", 25);

泛型方法

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

// 泛型方法可以自动推断类型
public static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
}

// 使用
int max = Utility.Max(10, 20); // 类型推断
string maxStr = Utility.Max<string>("a", "b"); // 显式指定

int x = 1, y = 2;
Utility.Swap(ref x, ref y); // 类型推断

泛型约束

泛型约束限制了可以用作类型参数的类型。

约束说明示例
where T : struct值类型int, double, DateTime
where T : class引用类型string, object, 自定义类
where T : class?可空引用类型string?
where T : new()有无参构造函数可以 new T()
where T : BaseClass继承特定基类必须是 BaseClass 或其子类
where T : IInterface实现特定接口必须实现 IInterface
where T : U两个类型参数有关系T 必须是 U 或其派生类
where T : notnull不可为 null非空类型

约束示例

// 值类型约束
public class NumberProcessor<T> where T : struct
{
public T Add(T a, T b)
{
// T 必须是值类型
dynamic x = a, y = b;
return x + y;
}
}

// 引用类型约束
public class EntityRepository<T> where T : class
{
public void Save(T entity)
{
// T 必须是引用类型
}
}

// new() 约束
public class Factory<T> where T : new()
{
public T Create()
{
return new T(); // 可以使用无参构造函数
}
}

// 接口约束
public class Sorter<T> where T : IComparable<T>
{
public void Sort(List<T> items)
{
items.Sort(); // T 必须实现 IComparable<T>
}
}

// 多个约束
public class AdvancedRepository<T>
where T : BaseEntity, IValidatable, new()
{
public T CreateAndValidate()
{
var entity = new T();
if (!entity.IsValid())
{
throw new InvalidOperationException();
}
return entity;
}
}

// 多个类型参数的约束
public class Converter<TSource, TTarget>
where TSource : class
where TTarget : class, new()
{
public TTarget Convert(TSource source)
{
var target = new TTarget();
// 转换逻辑
return target;
}
}

泛型接口

// 定义泛型接口
public interface IRepository<T> where T : class
{
Task<T> GetByIdAsync(int id);
Task<IEnumerable<T>> GetAllAsync();
Task AddAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(int id);
}

// 实现泛型接口
public class UserRepository : IRepository<User>
{
public async Task<User> GetByIdAsync(int id)
{
// 实现
}

public async Task<IEnumerable<User>> GetAllAsync()
{
// 实现
}

// ... 其他方法
}

// 泛型实现
public class GenericRepository<T> : IRepository<T> where T : class
{
private readonly DbContext _context;

public async Task<T> GetByIdAsync(int id)
{
return await _context.Set<T>().FindAsync(id);
}

// ... 其他方法
}

协变和逆变

协变 (Covariance) - out

协变

允许使用比原始指定更派生(更具体)的类型。

// 协变接口
public interface IProducer<out T>
{
T Produce();
}

public class AnimalProducer : IProducer<Animal>
{
public Animal Produce() => new Animal();
}

public class DogProducer : IProducer<Dog>
{
public Dog Produce() => new Dog();
}

// 使用协变
IProducer<Dog> dogProducer = new DogProducer();
IProducer<Animal> animalProducer = dogProducer; // ✅ 协变:Dog 是 Animal 的子类
Animal animal = animalProducer.Produce();

逆变 (Contravariance) - in

逆变

允许使用比原始指定更基础(更泛化)的类型。

// 逆变接口
public interface IConsumer<in T>
{
void Consume(T item);
}

public class AnimalConsumer : IConsumer<Animal>
{
public void Consume(Animal animal) => Console.WriteLine($"消费 {animal.Name}");
}

// 使用逆变
IConsumer<Animal> animalConsumer = new AnimalConsumer();
IConsumer<Dog> dogConsumer = animalConsumer; // ✅ 逆变:Animal 是 Dog 的父类
dogConsumer.Consume(new Dog());

实际应用

// IEnumerable<out T> 是协变的
IEnumerable<Dog> dogs = new List<Dog>();
IEnumerable<Animal> animals = dogs; // ✅ 协变

// Action<in T> 是逆变的
Action<Animal> animalAction = (animal) => Console.WriteLine(animal.Name);
Action<Dog> dogAction = animalAction; // ✅ 逆变

// Func<in T, out TResult> 同时支持协变和逆变
Func<Animal, Dog> func = (animal) => new Dog();
Func<Dog, Animal> func2 = func; // ✅ 参数逆变,返回值协变

实战案例

案例1:泛型仓储模式

public interface IEntity
{
int Id { get; set; }
}

public class BaseRepository<T> where T : class, IEntity
{
protected readonly DbContext _context;

public BaseRepository(DbContext context)
{
_context = context;
}

public virtual async Task<T> GetByIdAsync(int id)
{
return await _context.Set<T>().FindAsync(id);
}

public virtual async Task<List<T>> GetAllAsync()
{
return await _context.Set<T>().ToListAsync();
}

public virtual async Task<T> AddAsync(T entity)
{
await _context.Set<T>().AddAsync(entity);
await _context.SaveChangesAsync();
return entity;
}

public virtual async Task UpdateAsync(T entity)
{
_context.Entry(entity).State = EntityState.Modified;
await _context.SaveChangesAsync();
}

public virtual async Task DeleteAsync(int id)
{
var entity = await GetByIdAsync(id);
if (entity != null)
{
_context.Set<T>().Remove(entity);
await _context.SaveChangesAsync();
}
}
}

// 使用
public class User : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
}

var userRepo = new BaseRepository<User>(dbContext);
var user = await userRepo.GetByIdAsync(1);

案例2:泛型结果类型

public class Result<T>
{
public bool IsSuccess { get; private set; }
public T Data { get; private set; }
public string ErrorMessage { get; private set; }

private Result(bool isSuccess, T data, string errorMessage)
{
IsSuccess = isSuccess;
Data = data;
ErrorMessage = errorMessage;
}

public static Result<T> Success(T data)
{
return new Result<T>(true, data, null);
}

public static Result<T> Failure(string errorMessage)
{
return new Result<T>(false, default, errorMessage);
}

public TResult Match<TResult>(
Func<T, TResult> onSuccess,
Func<string, TResult> onFailure)
{
return IsSuccess ? onSuccess(Data) : onFailure(ErrorMessage);
}
}

// 使用
public async Task<Result<User>> GetUserAsync(int id)
{
try
{
var user = await _repository.GetByIdAsync(id);
return user != null
? Result<User>.Success(user)
: Result<User>.Failure("用户不存在");
}
catch (Exception ex)
{
return Result<User>.Failure(ex.Message);
}
}

// 调用
var result = await GetUserAsync(1);
var message = result.Match(
user => $"找到用户: {user.Name}",
error => $"错误: {error}"
);

案例3:泛型缓存

public interface ICache<T>
{
Task<T> GetAsync(string key);
Task SetAsync(string key, T value, TimeSpan? expiration = null);
Task RemoveAsync(string key);
}

public class MemoryCache<T> : ICache<T>
{
private readonly IMemoryCache _cache;

public MemoryCache(IMemoryCache cache)
{
_cache = cache;
}

public Task<T> GetAsync(string key)
{
_cache.TryGetValue(key, out T value);
return Task.FromResult(value);
}

public Task SetAsync(string key, T value, TimeSpan? expiration = null)
{
var options = new MemoryCacheEntryOptions();
if (expiration.HasValue)
{
options.SetAbsoluteExpiration(expiration.Value);
}
_cache.Set(key, value, options);
return Task.CompletedTask;
}

public Task RemoveAsync(string key)
{
_cache.Remove(key);
return Task.CompletedTask;
}
}

// 使用
var userCache = new MemoryCache<User>(_memoryCache);
await userCache.SetAsync("user:1", user, TimeSpan.FromMinutes(10));
var cachedUser = await userCache.GetAsync("user:1");

泛型最佳实践

最佳实践

1. 优先使用泛型而非 object

// ❌ 避免
public object GetValue() { }

// ✅ 推荐
public T GetValue<T>() { }

2. 合理使用约束

// 添加必要的约束,提供更好的类型安全
public class Service<T> where T : IEntity, new()
{
// 现在可以安全地使用 T 的成员
}

3. 考虑使用协变和逆变

// 如果类型参数只用于输出,使用 out
public interface IReadOnlyRepository<out T>
{
Task<T> GetByIdAsync(int id);
}

4. 避免过度泛型化

// ❌ 过度泛型化
public class Processor<T1, T2, T3, T4, T5> { }

// ✅ 合理设计
public class Processor<TInput, TOutput> { }

总结

关键要点
  • 泛型提供类型安全、性能优化和代码重用
  • 使用泛型约束限制类型参数
  • 理解协变(out)和逆变(in)的使用场景
  • 泛型广泛应用于集合、仓储模式、缓存等场景
  • 合理使用泛型,避免过度复杂化

相关资源