C#中如何检查泛型类型是否为另一类型的子类型?
给定泛型类型<T>,我希望检查它是否是另一个类型<Other>的子类型。原本以为T is Other可以实现该功能,但实际只有typeof(T).IsAssignableTo(typeof(Other))能正常运行。是否存在更优的实现方式?
示例代码如下:
public class Animal { } public class AnimalFactory { class FakeAnimal : Animal { } static FakeAnimal fakeCached = new FakeAnimal(); public static Animal CreateAnimal<T>() where T : Animal, new() { //if (T is FakeAnimal) // -> 无法编译 if (typeof(T).IsAssignableTo(typeof(FakeAnimal))) // -> 运行正常 { return fakeCached ; } return new T(); } }
注:上述示例已简化,实际业务场景更为复杂,仅关注无法编译的代码行。
T is FakeAnimal编译失败的核心原因:T是类型参数,不是具体的实例对象,而is运算符的作用是判断实例是否兼容某个类型,不能直接用在类型本身,所以写法不符合语法规则。关于更优实现,分两种场景选择:
编译时约束(适合固定场景)
如果业务逻辑中,只有当
T是FakeAnimal或其子类时才需要走缓存逻辑,可以直接通过泛型约束限定,不需要运行时判断:// 仅允许T为FakeAnimal或其子类时调用 public static Animal CreateFakeAnimal<T>() where T : FakeAnimal, new() { return fakeCached; }这种方式性能最好,但灵活性差,只适用于规则固定的场景。
运行时检查(适合动态场景)
你当前使用的
typeof(T).IsAssignableTo(typeof(FakeAnimal))已经是.NET中判断类型兼容性的标准方案,它能正确处理以下情况:T与FakeAnimal类型完全相同T是FakeAnimal的子类- 如果
FakeAnimal是接口,T实现了该接口
另外,也可以写成
typeof(FakeAnimal).IsAssignableFrom(typeof(T)),逻辑完全等价,只是写法相反,看个人习惯选择。有人可能会尝试用
default(T) is FakeAnimal来判断,但这种方式不推荐:因为default(T)对于引用类型是null,null is FakeAnimal会返回false;如果借助new()约束创建实例判断,又会额外产生对象实例,带来性能开销,甚至可能触发构造函数的副作用。
综上,typeof(T).IsAssignableTo(typeof(Other))就是当前动态场景下最合理、高效的实现方式,没有更优的替代方案。
内容的提问来源于stack exchange,提问作者gaps

