泛型是编译期与运行时协同的类型机制,非语法糖;错误约束、开放/封闭类型混用、忽略default(t)语义均会导致编译或运行时错误。

泛型不是语法糖,它是编译期+运行时共同参与的类型系统机制——用错约束、混用开放/封闭类型、忽略默认值语义,都会在编译或运行时咬你一口。
泛型类型参数不能直接 new(),除非加 where T : new()
你写 new T() 会直接报错 CS0304:“无法创建类型参数的实例”,因为编译器不知道 T 是否有无参构造函数。这不是“忘了引用命名空间”那种低级错误,而是类型系统在拦你。
必须显式声明约束:
public class Factory<t> where T : new()
{
public T Create() => new T(); // ✅ 编译通过
}</t>
-
where T : new()不代表T是类——struct也可以带无参构造函数(如DateTime),但自定义struct默认不带,需手动定义 -
where T : class, new()才真正排除值类型,强制为引用类型且可实例化 - 如果只写
where T : new()却传入int,编译失败;传入string也失败——string没有 public 无参构造函数
where T : class 不是“限制为类”,而是“禁止值类型”
where T : class 的真实含义是“T 必须是引用类型”,包括:类、接口、委托、数组、string、object,甚至 dynamic——但绝不是“只能是 class 声明的类”。
常见误判场景:
- 传
Guid报错?对,Guid是struct,值类型 → 违反约束 - 传
IEnumerable<int></int>成功?对,接口是引用类型 - 传
int[]成功?对,数组类型在 CLR 中是引用类型 - 传
Nullable<int></int>(即int?)失败?对,它是struct,不是引用类型
这个约束常被用于避免装箱,或确保 T 可以为 null(比如做缓存键判断:if (instance == null) 在值类型上永远为 false)。
泛型方法里的 default(T) 不等于 null
default(T) 的行为完全取决于 T 是值类型还是引用类型——它不是“空引用”,而是类型的零值:
-
default(string)→null -
default(int)→0 -
default(bool)→false -
default(MyClass)→null -
default(MyStruct)→ 所有字段初始化为各自default值(递归展开)
所以别在泛型方法里写 if (value == null) 判断通用参数,它对值类型永远不成立。正确姿势是:
if (EqualityComparer<t>.Default.Equals(value, default(T))) { ... }</t>
或者更轻量地用 if (value?.ToString() != null)(仅当 T 确保可空或已知为引用类型时)。
泛型嵌套类和开放类型容易踩 runtime 反射坑
泛型嵌套类(比如 Outer<t>.Inner<u></u></t>)在反射中会暴露为开放类型(Outer`1+Inner`1),此时 Type.GetGenericArguments() 返回的是占位符(T, U),不是真实类型。
这意味着:
-
typeof(Outer<int>.Inner<string>).IsGenericTypeDefinition</string></int>→false(已封闭) -
typeof(Outer.Inner).IsGenericTypeDefinition→true(纯开放) - 用
MakeGenericType(...)构造时,必须按声明顺序传入所有类型参数,漏一个就抛ArgumentException -
typeof(List).GetGenericArguments()返回长度为 1 的数组,元素是System.RuntimeType(表示未绑定的T),不是typeof(int)
这种差异在序列化、DI 容器注册、ORM 映射等依赖反射的场景中极易出错,且错误信息往往模糊(比如 “无法解析泛型参数”)。最稳妥的做法:只对已构造类型(如 typeof(List<int>)</int>)做反射操作,避免碰开放类型。











