C#泛型底层揭秘
2026/9/2 5:48:05 网站建设 项目流程

1. 泛型本质(CLR 底层视角)

  • 定义:泛型是类型参数化机制。在定义时不指定具体类型,用占位符(如T)代替;在实例化/调用时传入具体类型。

  • CLR 执行时机:泛型是运行时(JIT)真正落地的。

    • 值类型(struct):JIT 会为每一种不同的值类型参数生成一份独立的本机代码(例如List<int>List<long>是两份完全不同的机器码)。

    • 引用类型(class):JIT 会共用一份本机代码(例如List<string>List<object>List<Stream>共用同一份代码,因为引用类型指针大小固定,占位由运行时替换)。

  • 核心性能收益:避免值类型的装箱/拆箱(零开销抽象),同时保留编译时类型检查。


2. 泛型种类(语法落点)

泛型可以附着在5种类型上:

附着对象示例说明
泛型类class MyClass<T> { }最常用,类级全局占位
泛型结构struct MyStruct<T> { }值类型也能泛型化
泛型接口interface IMy<T> { }约束行为,支持协变/逆变
泛型方法void MyMethod<T>(T arg) { }不必依赖泛型类,可单独存在
泛型委托delegate T MyDelegate<T>(T arg);Func<T, TResult>

高频坑:泛型方法可以存在于非泛型类中,且编译器能通过参数自动推断类型(不必显式写<T>)。


3. 类型约束(where子句)—— 面试必问

约束用于限制T必须满足的条件,不约束则只能使用object的方法(ToStringEqualsGetHashCode)。

约束写法含义关键限制
where T : structT 必须是值类型(不可为 null)不能是Nullable<T>(如int?
where T : classT 必须是引用类型可以是接口、类、数组等
where T : class?(C# 8+)T 是可空引用类型主要用于可空上下文
where T : notnull(C# 8+)T 是不可空类型(值或引用)排除string?int?
where T : unmanaged(C# 7.3+)T 是非托管类型(不含引用)常用于Span<T>、内存操作、P/Invoke
where T : new()T 必须有无参构造函数必须放在约束链的最后一位
where T : BaseClassT 必须是基类或派生类可调用基类成员
where T : IInterfaceT 必须实现指定接口可调用接口方法
where T : UT 必须继承自另一个泛型参数 U极少用,解决类型关联

组合约束示例(含顺序)

csharp

class MyClass<T> where T : class, IComparable, new() // class在前,new()必须在最后 { }

4. 静态成员与泛型 —— 最大认知盲区

结论泛型类的静态成员,是按“闭合类型”独立存储的。

  • MyClass<int>.StaticValueMyClass<string>.StaticValue两个完全独立的静态变量,互不影响。

  • 面试经典题

    csharp

    class MyClass<T> { public static int Count; } MyClass<int>.Count = 10; MyClass<string>.Count = 20; Console.WriteLine(MyClass<int>.Count); // 输出 10(与string的20毫无关系)
  • 原因:JIT 在运行时为每个不同的闭合类型生成不同的静态字段内存区域。


5. 协变(out)与 逆变(in)—— 高级多态

仅适用于泛型接口泛型委托,不适用于泛型类。

关键字名称作用方向示例接口允许操作
out协变返回值(产出)IEnumerable<out T>允许IEnumerable<string>赋给IEnumerable<object>
in逆变参数(消费)Action<in T>允许Action<object>赋给Action<string>

记忆口诀out 是产出(返回值),in 是收入(参数)

重要限制:协变/逆变只能用于引用类型,值类型不生效(IEnumerable<int>不能转IEnumerable<object>)。


6.default关键字在泛型中的妙用

在不知道T具体是值还是引用时,用default(T)或简写default获取默认值:

  • 值类型:返回 0、false或空结构体。

  • 引用类型:返回null


7. 泛型与反射/类型获取

  • 获取未绑定泛型(开放类型):typeof(MyClass<>)(不留占位符)。

  • 获取已绑定泛型(闭合类型):typeof(MyClass<int>)

  • 运行时创建泛型实例:

    csharp

    Type type = typeof(MyClass<>).MakeGenericType(typeof(string)); object obj = Activator.CreateInstance(type);

8. 泛型约束的局限性(必须知道的坑)

  • 不支持运算符重载约束:无法约束T必须有+*运算符,因此泛型数学运算很麻烦(需借助System.Numerics.INumber<T>接口(C# 11+ .NET 7+))。

  • 不支持枚举约束:不能写where T : enum(.NET 早期限制,现可用where T : struct, Enum绕行)。

  • 不支持委托约束:不能写where T : Delegate(同样可用where T : class?绕行,但无法约束具体委托类型)。


9. 性能特别提示(面试加分项)

  • 数组协变有风险object[] arr = new string[10];这种写法是数组协变(非泛型),运行时赋值int会抛出异常。泛型集合List<T>是安全的,强烈替代数组协变

  • List<object>List<string>不是父子关系(不变性),必须通过IEnumerable<out T>实现协变。


10. 终极避坑口诀(泛型版)

  • 值类型泛型:独立机器码,零装箱。

  • 引用类型泛型:共享机器码,省内存。

  • 静态成员:闭合成一家,相互不干扰。

  • out产出可向上(子转父),in收入可向下(父转子)

  • new()约束:必须垫底写。

  • default不迷茫,值零引用空

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询