如何检查一个类型是否是子类型或对象的类型?

408

在C#中检查一个类型是否是另一个类型的子类很容易:

typeof (SubClass).IsSubclassOf(typeof (BaseClass)); // returns true

然而,这会失败:

typeof (BaseClass).IsSubclassOf(typeof (BaseClass)); // returns false

有没有一种方法可以在不使用OR运算符或扩展方法的情况下检查一个类型是否是子类或基类本身?

5个回答

614

显然不行。

以下是选项:

Type.IsSubclassOf

正如你已经发现的那样,如果两个类型相同,这种方法将不起作用。以下是一个演示的LINQPad程序:

void Main()
{
    typeof(Derived).IsSubclassOf(typeof(Base)).Dump();
    typeof(Base).IsSubclassOf(typeof(Base)).Dump();
}

public class Base { }
public class Derived : Base { }

输出:

True
False

这表明DerivedBase的一个子类,但Base(显然)不是它本身的子类。

Type.IsAssignableFrom

虽然这种方法将会回答你的具体问题,但也会给你返回错误的结果。正如Eric Lippert在评论中所指出的那样,虽然该方法确实会对上述两个问题返回True,但它也会对以下情况返回True,这可能并非你想要的:

void Main()
{
    typeof(Base).IsAssignableFrom(typeof(Derived)).Dump();
    typeof(Base).IsAssignableFrom(typeof(Base)).Dump();
    typeof(int[]).IsAssignableFrom(typeof(uint[])).Dump();
}

public class Base { }
public class Derived : Base { }

以下是您将获得的输出:

True
True
True
最后一个True表明,如果该方法仅回答所提出的问题,那么uint[]是否继承自int[]或它们是相同类型的,显然不是这种情况。因此,IsAssignableFrom也不完全正确。 isas 在你的问题的背景下,isas的"问题"在于它们要求你操作对象并直接在代码中写入一个类型,而不是使用Type对象。换句话说,以下代码将无法编译:
SubClass is BaseClass
^--+---^
   |
   +-- need object reference here

也不会这样:

typeof(SubClass) is typeof(BaseClass)
                    ^-------+-------^
                            |
                            +-- need type name here, not Type object

也不会这样:

typeof(SubClass) is BaseClass
^------+-------^
       |
       +-- this returns a Type object, And "System.Type" does not
           inherit from BaseClass

结论

尽管上述方法可能符合您的需求,但我认为对于您的问题,唯一正确的答案是您需要进行额外的检查:

typeof(Derived).IsSubclassOf(typeof(Base)) || typeof(Derived) == typeof(Base);
当然,在方法中这样做更有意义。
public bool IsSameOrSubclass(Type potentialBase, Type potentialDescendant)
{
    return potentialDescendant.IsSubclassOf(potentialBase)
           || potentialDescendant == potentialBase;
}

2
谢谢!我会将这个标记为正确答案(还需要等待8分钟),因为您提到了检查必须被反转,并提供了MSDN文档的链接。 - Daniel T.
83
请注意,这实际上并没有做出问题所要求的内容;这并不能确定一个类型是否是另一个类型的子类,而是确定一个类型是否与另一个类型可以互相赋值。一个uint数组不是int数组的子类,但它们可以互相赋值。IEnumerable<Giraffe>不是IEnumerable<Animal>的子类,在v4中它们可以互相赋值。 - Eric Lippert
3
IsInstanceOfType 应该如何适配这个场景? - Lennart
1
因为类不是接口的子类,而是实现。使用typeof(Test).GetInterfaces().Contains(typeof(ITest))来测试一个类是否实现了一个接口,或者使用typeof(ITest).IsAssignableFrom(typeof(Test)) - Lasse V. Karlsen
因为IsAssignableFrom回答的问题与“一个类型是否是另一个类型的子类型”这种类型的问题不同。它试图回答的是a = b是否有效。我已经有一段时间没有看过这些东西了,也不知道为什么在这里IsAssignableFrom返回true,因为实际上你不能直接将uint[]数组赋值给int[]变量,反之亦然,但我猜可能在某个地方可以进行一些强制转换。无论如何,int[]不是uint[]的子类型,反之亦然,所以使用IsAssignableFrom来回答这个问题是不正确的。不过,对于你的需求来说可能已经足够接近了。 - undefined
显示剩余2条评论

43
typeof(BaseClass).IsAssignableFrom(unknownType);

12

1
如果您正在尝试在Xamarin Forms PCL项目中执行此操作,则使用上述解决方案的IsAssignableFrom会出现错误:

“错误:'Type'不包含“IsAssignableFrom”的定义,也没有接受第一个参数类型为'Type'的扩展方法'IsAssignableFrom'可用(是否缺少使用指令或程序集引用?)”

因为IsAssignableFrom需要一个TypeInfo对象。您可以使用System.ReflectionGetTypeInfo()方法:

typeof(BaseClass).GetTypeInfo().IsAssignableFrom(typeof(unknownType).GetTypeInfo())


0
我发表这个答案的目的是希望有人与我分享,如果这样做是一个坏主意,为什么会是这样。在我的应用程序中,我有一个类型属性,我想检查它是否为typeof(A)或typeof(B),其中B是从A派生的任何类。所以我的代码:
public class A
{
}

public class B : A
{
}

public class MyClass
{
    private Type _helperType;
    public Type HelperType
    {
        get { return _helperType; }
        set 
        {
            var testInstance = (A)Activator.CreateInstance(value);
            if (testInstance==null)
                throw new InvalidCastException("HelperType must be derived from A");
            _helperType = value;
        }
    }
}

我觉得我可能有点天真,所以欢迎任何反馈。


4
这个想法有几个问题:1)类型需要一个无参数的构造函数,否则CreateInstance会失败;2)强制转换为(A)如果无法进行转换,不会返回null,而是会抛出异常;3)实际上您并不需要新的实例,所以您做了一次无用的分配。被接受的答案更好(虽然不完美)。 - Marcel Popescu
感谢反馈,非常有帮助。 - baskren

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接