泛型
大约 15 分钟约 4468 字
泛型
泛型引入
泛型声明
泛型的特点+原理
泛型的优势 ,核心设计
泛型约束
public static void ShowObject(object oParameter)
{
//Console.WriteLine("This is {0},parameter={1},type={2}",
// typeof(GenericConstraint), oParameter.GetType().Name, oParameter);
//传递一个实体对象:操作字段和数据
//问题:
//1.无法去属性字段--因为oParameter是Object; C#是强类型语言,编译时决定参数是什么类型;
//2.强制转换
//Console.WriteLine($"People.Id={oParameter.Id}");
//Console.WriteLine($"People.Name={oParameter.Name}");
People people = (People)oParameter;
Console.WriteLine($"People.Id={people.Id}");
Console.WriteLine($"People.Name={people.Name}");
}/// <summary> /// 1.基类约束 /// a.就是把类型参数当做People /// b.调用---就可以传递Popple或者People的子类型 /// c.泛型约束:要么不让你进来,如果让你进来,就一定是没有问题 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="tParameter"></param> public static void ShowBase<T>(T tParameter) where T : People // ,ISports { Console.WriteLine($"People.Id={tParameter.Id}"); Console.WriteLine($"People.Name={tParameter.Name}"); //tParameter.Pingpang(); }/// <summary> /// 接口约束: /// a.把这个T 当做ISports /// b.就只能传递ISports 这个接口或者是实现过这个接口的类 /// c.就可以增加功能,可以获取新的功能 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="tParameter"></param> public static void ShowInterface<T>(T tParameter) where T : ISports { tParameter.Pingpang(); }/// <summary> /// 引用类型约束 /// a.就只能传递类型进来 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="tParameter"></param> public static void ShowClass<T>(T tParameter) where T : class { //T t = new T(); //因为T 可能没有无参数构造构造函数 }/// <summary> /// 值类型约束 /// a.就只能传递值类型进来 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="tParameter"></param> public static void ShowStruct<T>(T tParameter) where T : struct { T t = new T(); }/// <summary> /// 无参数构造函数约束 /// a. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="tParameter"></param> public static void ShowNew<T>(T tParameter) where T : new() { T t = new T(); }/// <summary> /// 枚举约束 /// a.必须是个枚举才能够传递进来 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="tParameter"></param> public static void ShowEnum<T>(T tParameter) where T : Enum { }
泛型约束,泛型缓存
/// <summary>
/// 字典缓存:静态属性常驻内存
/// </summary>
public class DictionaryCache
{
private static Dictionary<Type, string> _TypeTimeDictionary = null;
//静态构造函数在整个进程中,执行且只执行一次;
static DictionaryCache()
{
Console.WriteLine("This is DictionaryCache 静态构造函数");
_TypeTimeDictionary = new Dictionary<Type, string>();
}
public static string GetCache<T>()
{
Type type = typeof(T);
if (!_TypeTimeDictionary.ContainsKey(type))
{
_TypeTimeDictionary[type] = $"{typeof(T).FullName}_{DateTime.Now.ToString("yyyyMMddHHmmss.fff")}";
}
return _TypeTimeDictionary[type];
}
}协变,逆变(是一种高级约束,是为了规避)
/// <summary>
/// out 协变covariant 修饰返回值
/// in 逆变contravariant 修饰传入参数
/// </summary>
public class ContravariantCovariance
{
public delegate T delegateTest<T>(T t);
/// <summary>
/// 协变逆变 只针对于泛型接口和泛型委托的
/// </summary>
public static void Show()
{
{
Animal animal1 = null;
animal1 = new Animal();
Cat cat1 = null;
cat1 = new Cat();
//任何子类都可以使用父类来声明
Animal animal2 = null;
animal2 = new Cat();
///不一定--
//Cat cat2 = null;
//cat2 = new Animal();
//Cat cat2 = null;
//cat2 = (Cat)(new Animal());
}
{
List<Animal> animalList1 = null;
animalList1 = new List<Animal>();
//一只猫是一个动物
//一堆猫 却不是一堆动物---从口语上来说,有点不符合人类的思维逻辑;
//Why?---二者没有父子级关系;当然不能替换; 这是C#语法所决定的
//泛型存在不友好,不协调的地方;
//不能在左边用父类;
//List<Animal> animalList2 = null; //--- List<Animal> 针对于Animal的类
//animalList2 = new List<Cat>(); //--- List<Cat>针对于Cat的类
// List<Animal> animalList3 = new List<Cat>().Select(c => (Animal)c).ToList();
}
//就引入了协变和逆变
{
//IEnumerable 也经常把他当成一个集合来用
//协变 就可以让右边用子类,能让左边用父类
//out:修饰类型参数;就可以让右边用子类,能让左边用父类
IEnumerable<Animal> animalList1 = new List<Animal>();
IEnumerable<Animal> animalList2 = new List<Cat>();
Func<Animal> func = new Func<Cat>(() => null);
//协变: Out 类型参数只能做返回值 ,不能做参数
ICustomerListOut<Animal> customerList1 = new CustomerListOut<Animal>();
ICustomerListOut<Animal> customerList2 = new CustomerListOut<Cat>(); //协变
//customerList2.Show(new Animal());
//customerList2.Show(new Cat());
}
{//逆变 In 只能做参数 ,不能做返回值
//逆变:就可以让右边用父类;左边用子类
ICustomerListIn<Cat> customerList2 = new CustomerListIn<Cat>();
ICustomerListIn<Cat> customerList1 = new CustomerListIn<Animal>();
//逆变: In 类型参数只能做参数 ,不能做返回值
//customerList1.Get();//调用的是接口的方法
//customerList1.Get(); //返回的一定是一个Cat 或者是Cat 的子类;
//因为通过接口在调用方法的时候只能返回一个Cat,
}
//协变逆变的存在,就是为了满足常规场景添加一个避开风险的约束;
{
IMyList<Cat, Animal> myList1 = new MyList<Cat, Animal>();
IMyList<Cat, Animal> myList2 = new MyList<Cat, Cat>();//协变
IMyList<Cat, Animal> myList3 = new MyList<Animal, Animal>();//逆变
IMyList<Cat, Animal> myList4 = new MyList<Animal, Cat>();//协变+逆变
}
//为什么呢?
//如果没有协变逆变;会如何?
{
}
}
}
public class Test : ICustomerListOut<Animal>
{
public Animal Get()
{
return new Cat();
}
public void Show(Animal t)
{
}
}
public class Test2 : ICustomerListOut<Cat>
{
public Cat Get()
{
throw new NotImplementedException();
}
public void Show(Cat t)
{
throw new NotImplementedException();
}
}
/// <summary>
/// 动物
/// </summary>
public class Animal
{
public int Id { get; set; }
}
/// <summary>
/// Cat 猫
/// </summary>
public class Cat : Animal
{
public string Name { get; set; }
}
/// <summary>
/// T 就只能做参数 不能做返回值
/// </summary>
/// <typeparam name="T"></typeparam>
public interface ICustomerListIn<in T>
{
//T Get();
void Show(T t);
}
public class CustomerListIn<T> : ICustomerListIn<T>
{
//public T Get()
//{
// return default(T);
//}
public void Show(T t)
{
}
}
/// <summary>
/// out 协变 只能是返回结果 ,还是int 也是一种高级约束,避免出现问题
/// 泛型T 就只能做返回值; 不能做参数;
/// </summary>
/// <typeparam name="T"></typeparam>
public interface ICustomerListOut<out T>
{
T Get();
//void Show(T t);
}
public class CustomerListOut<T> : ICustomerListOut<T>
{
public T Get()
{
return default(T);
}
public void Show(T t) //t 是Cat的时候,这会儿你给我传递了一个Animal进来,子类做参数,但是传递了一个父类简历
{
}
}
public interface IMyList<in inT, out outT>
{
void Show(inT t);
outT Get();
outT Do(inT t);
}
/// <summary>
/// out 协变 只能是返回结果
/// in 逆变 只能是参数
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
public class MyList<T1, T2> : IMyList<T1, T2>
{
public void Show(T1 t)
{
Console.WriteLine(t.GetType().Name);
}
public T2 Get()
{
Console.WriteLine(typeof(T2).Name);
return default(T2);
}
public T2 Do(T1 t)
{
Console.WriteLine(t.GetType().Name);
Console.WriteLine(typeof(T2).Name);
return default(T2);
}
}泛型缓存深入
泛型类的静态字段缓存
/// <summary>
/// 泛型类的静态字段在每个类型参数组合上各自独立
/// CLR 为每个不同的类型参数创建独立的静态字段副本
/// </summary>
public class GenericCache<T>
{
// 每个 T 类型有独立的静态字段副本
private static string? _cache;
private static readonly object _lock = new();
public static string GetCache()
{
if (_cache == null)
{
lock (_lock)
{
_cache ??= $"{typeof(T).FullName}_{DateTime.Now:yyyyMMddHHmmss.fff}";
}
}
return _cache;
}
}
// 使用示例 — 每个类型参数有独立缓存
Console.WriteLine(GenericCache<int>.GetCache()); // 独立缓存
Console.WriteLine(GenericCache<string>.GetCache()); // 独立缓存
Console.WriteLine(GenericCache<int>.GetCache()); // 返回与第一次相同泛型缓存的应用场景
/// <summary>
/// 使用泛型缓存实现高性能类型映射
/// 避免每次调用都进行反射
/// </summary>
public static class TypeMapperCache<T>
{
// 静态构造函数 + 泛型 = 每个类型只初始化一次
public static readonly Func<T> DefaultFactory;
public static readonly Action<T, object?>[] PropertySetters;
static TypeMapperCache()
{
// 为类型 T 预编译委托(避免运行时反射)
var type = typeof(T);
// 无参构造函数
var ctor = type.GetConstructor(Type.EmptyTypes);
if (ctor != null)
{
DefaultFactory = (Func<T>)Delegate.CreateDelegate(
typeof(Func<T>), ctor);
}
else
{
DefaultFactory = () => default!;
}
// 属性设置器缓存
var properties = type.GetProperties()
.Where(p => p.CanWrite)
.ToArray();
PropertySetters = new Action<T, object?>[properties.Length];
for (int i = 0; i < properties.Length; i++)
{
var prop = properties[i];
PropertySetters[i] = (obj, value) => prop.SetValue(obj, value);
}
}
}泛型与反射
运行时泛型操作
using System.Reflection;
// 通过反射创建泛型类型
var openGeneric = typeof(List<>);
var closedGeneric = openGeneric.MakeGenericType(typeof(int));
var instance = Activator.CreateInstance(closedGeneric)!;
// 通过反射调用泛型方法
var method = typeof(MyClass).GetMethod("MyGenericMethod")!;
var genericMethod = method.MakeGenericMethod(typeof(string));
genericMethod.Invoke(null, new object[] { "hello" });
// 检查泛型类型定义
var listType = typeof(List<int>);
Console.WriteLine(listType.IsGenericType); // True
Console.WriteLine(listType.GetGenericTypeDefinition() == typeof(List<>)); // True
Console.WriteLine(listType.GetGenericArguments()[0]); // System.Int32
// 泛型约束检查
var openType = typeof(Dictionary<,>);
var args = openType.GetGenericArguments();
foreach (var arg in args)
{
Console.WriteLine($"{arg.Name}: ");
var constraints = arg.GetGenericParameterConstraints();
foreach (var c in constraints)
Console.WriteLine($" 约束: {c.Name}");
var flags = arg.GenericParameterAttributes;
if (flags.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint))
Console.WriteLine(" class 约束");
if (flags.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint))
Console.WriteLine(" struct 约束");
if (flags.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint))
Console.WriteLine(" new() 约束");
}泛型高级模式
递归泛型约束(CRTP 模式)
/// <summary>
/// 递归泛型约束 — 用于 Fluent API 和 Builder 模式
/// 让基类能够返回子类类型,保持链式调用的类型安全
/// </summary>
public abstract class Entity<TSelf> where TSelf : Entity<TSelf>
{
public int Id { get; set; }
// 返回 TSelf 而非 Entity,保持子类类型信息
public TSelf WithId(int id)
{
Id = id;
return (TSelf)this;
}
}
public class User : Entity<User>
{
public string Name { get; set; } = "";
public string Email { get; set; } = "";
public User WithName(string name) { Name = name; return this; }
public User WithEmail(string email) { Email = email; return this; }
}
// 链式调用保持类型安全
var user = new User()
.WithId(1)
.WithName("张三")
.WithEmail("zhang@example.com");泛型工厂模式
/// <summary>
/// 类型安全的泛型工厂 — 结合泛型约束实现灵活的对象创建
/// </summary>
public interface IFactory<out T>
{
T Create();
}
public class GenericFactory<T> : IFactory<T> where T : new()
{
public T Create() => new T();
}
// 注册式工厂
public class ServiceFactory
{
private static readonly Dictionary<Type, Func<object>> _creators = new();
public static void Register<TInterface, TImplementation>()
where TImplementation : TInterface, new()
{
_creators[typeof(TInterface)] = () => new TImplementation();
}
public static TInterface Create<TInterface>()
{
if (_creators.TryGetValue(typeof(TInterface), out var creator))
{
return (TInterface)creator();
}
throw new InvalidOperationException(
$"未注册类型 {typeof(TInterface).Name}");
}
}
// 使用
ServiceFactory.Register<IRepository, SqlRepository>();
ServiceFactory.Register<ILogger, FileLogger>();
var repo = ServiceFactory.Create<IRepository>();
var logger = ServiceFactory.Create<ILogger>();泛型与 Span/Memory(高性能场景)
// 泛型方法处理 Span<T> — 零拷贝操作
public static class SpanHelper
{
// 通用数组切片(无装箱)
public static Span<T> Slice<T>(T[] array, int start, int length)
{
return array.AsSpan(start, length);
}
// 泛型数值计算(使用 INumber<T> 约束)
public static T Sum<T>(ReadOnlySpan<T> values) where T : INumber<T>
{
T sum = T.Zero;
foreach (var v in values)
{
sum += v;
}
return sum;
}
// 泛型类型转换
public static void Convert<TSource, TDestination>(
ReadOnlySpan<TSource> source, Span<TDestination> destination)
where TSource : ISpanParsable<TSource>
where TDestination : ISpanParsable<TDestination>
{
for (int i = 0; i < source.Length; i++)
{
// 逐元素转换逻辑
}
}
}泛型与 async/await
/// <summary>
/// 异步泛型模式 — 泛型仓储和服务层
/// </summary>
public interface IRepository<TEntity, TKey>
where TEntity : class
where TKey : notnull
{
Task<TEntity?> GetByIdAsync(TKey id);
Task<IReadOnlyList<TEntity>> GetAllAsync();
Task<TEntity> AddAsync(TEntity entity);
Task UpdateAsync(TEntity entity);
Task DeleteAsync(TKey id);
Task<bool> ExistsAsync(TKey id);
}
public class InMemoryRepository<TEntity, TKey> : IRepository<TEntity, TKey>
where TEntity : class, IEntity<TKey>
where TKey : notnull
{
private readonly Dictionary<TKey, TEntity> _store = new();
public Task<TEntity?> GetByIdAsync(TKey id)
{
_store.TryGetValue(id, out var entity);
return Task.FromResult(entity);
}
public Task<IReadOnlyList<TEntity>> GetAllAsync()
{
IReadOnlyList<TEntity> result = _store.Values.ToList().AsReadOnly();
return Task.FromResult(result);
}
public Task<TEntity> AddAsync(TEntity entity)
{
_store[entity.Id] = entity;
return Task.FromResult(entity);
}
public Task UpdateAsync(TEntity entity)
{
_store[entity.Id] = entity;
return Task.CompletedTask;
}
public Task DeleteAsync(TKey id)
{
_store.Remove(id);
return Task.CompletedTask;
}
public Task<bool> ExistsAsync(TKey id)
{
return Task.FromResult(_store.ContainsKey(id));
}
}
public interface IEntity<T>
{
T Id { get; set; }
}泛型性能考量
装箱拆箱与泛型对比
// 性能对比 — 泛型 vs 非泛型
public static class PerformanceComparison
{
// 非泛型方式(有装箱)
public static ArrayList CreateListNonGeneric(int count)
{
var list = new ArrayList(count);
for (int i = 0; i < count; i++)
{
list.Add(i); // 装箱:int -> object
}
return list;
}
// 泛型方式(无装箱)
public static List<int> CreateListGeneric(int count)
{
var list = new List<int>(count);
for (int i = 0; i < count; i++)
{
list.Add(i); // 无装箱
}
return list;
}
// 泛型方法避免虚方法调用(值类型特化)
public static T Max<T>(T a, T b) where T : IComparable<T>
{
return a.CompareTo(b) >= 0 ? a : b;
}
// 对于值类型,JIT 会生成特化代码,避免接口调度开销
}关键知识点
- 先明确这个主题影响的是语法层、运行时层,还是性能与可维护性层。
- 学习时要同时关注语言表面写法和编译器、JIT、GC 等底层行为。
- 真正有价值的是知道“为什么这样写”和“在什么边界下不能这样写”。
项目落地视角
- 把示例改成最小可运行样例,并观察编译输出、运行结果和异常行为。
- 如果它会进入团队代码规范,最好同步补充命名约定、禁用场景和替代方案。
- 涉及性能结论时,优先用 Benchmark 或实际热点链路验证,而不是凭感觉判断。
常见误区
- 只记语法糖,不知道底层成本。
- 把适用于小样例的写法直接搬到高并发或大对象场景里。
- 忽略框架版本、语言版本和运行时差异,导致结论失真。
进阶路线
- 继续向源码、IL、JIT 行为和 BCL 实现层深入。
- 把知识点和代码评审、性能诊断、面试复盘结合起来。
- 把同类主题做横向对比,例如值类型与引用类型、迭代器与 async 状态机、反射与 Source Generator。
适用场景
- 当你准备把《泛型》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合在需要理解语言特性、运行时行为或 API 边界时阅读。
- 当代码开始出现性能瓶颈、可维护性问题或语义歧义时,这类主题会直接影响实现质量。
落地建议
- 先写最小可运行样例,再把结论迁移到真实业务代码。
- 同时记录这个特性的收益、限制和替代方案,避免为了“高级”而使用。
- 涉及内存、并发或序列化时,最好配合调试器或基准测试验证。
排错清单
- 先确认问题属于编译期、运行期还是语义误用。
- 检查是否存在隐式转换、装箱拆箱、闭包捕获或上下文切换等隐藏成本。
- 查看异常栈、日志和最小复现代码,优先排除使用姿势问题。
复盘问题
- 如果把《泛型》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《泛型》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《泛型》最大的收益和代价分别是什么?
