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