T是泛型类型参数占位符,非固定类型或关键字;编译时替换为具体类型生成专用代码,兼顾类型安全与性能,避免object装箱和dynamic失去编译检查。
T 就是你用的时候才填进去的类型占位符,不是某种固定类型,也不是关键字,它就是个“代号”。 比如 List 本身不能直接 new,你必须写成 List 或 List —— 这时 T 才被替换成具体类型,编译器才真正生成对应代码。
用 object 看似通用,但会引发两个硬伤:
int)存进 List 会装箱,取出来再拆箱,性能损耗明显,尤其高频操作时
类型不安全:你往里加了个 string,编译器不管;但后面按 int 强转,运行时直接抛 InvalidCastException
dynamic 更糟:完全绕过编译检查,连 IDE 提示、重构支持都丢了而 List 在编译期就锁死类型——list.Add("abc") 往 List 里加,根本过不了编译。
完全可以。T 只是约定俗成的单字母缩写,源于 “Type”。你写 class Box、class Repository 甚至 class Pipe 都合法。
但要注意:
Dictionary,没人会写 Dictionary
IComparer、Predicate、Func
T 最省事,团队也一眼能懂泛型方法把类型选择权交给调用方,而不是类本身。例如:
public static T GetFirst(IList list) { return list.Count > 0 ? list[0] : default; }
调用时可以显式指定:GetFirst,也可以让编译器推断:GetFirst(myInts)(前提是 myInts 是 List 这类明确类型的集合)。
容易踩的坑:
T 做 == 或 != 判断(值类型/引用类型行为不一致),改用 EqualityComparer.Default.Equals(a, b)
T 必须有无参构造函数,得加 where T : new()
typeof(T) == typeof(int) 来做运行时分支——这违背泛型本意,也影响 JIT 优化真正关键的一点是:T 不是运行时“动态决定”的类型,而是编译时由你填写后,CLR 为每种实际类型(int、Customer、Guid)生成独立的专用版本。所以它既类型安全,又没装箱开销——这不是妥协出来的方案,是 C# 泛型设计的底层逻辑。