IsAssignableFrom、IsInstanceOfType 和 is 关键字,它们有什么区别?

49

我有一个安全转换对象的扩展方法,代码如下:

public static T SafeCastAs<T>(this object obj) {
    if (obj == null)
        return default(T);

    // which one I should use?

    // 1. IsAssignableFrom
    if (typeof(T).IsAssignableFrom(obj.GetType()))
        return (T)obj;

    // 2. IsInstanceOfType
    if (typeof(T).IsInstanceOfType(obj))
        return (T) obj;

    // 3. is operator
    if (obj is T)
        return (T) obj;

    return default(T);
}

你可以看到,我有三个选择,那么我应该使用哪一个?实际上,IsAssignableFromIsInstanceOfTypeis运算符之间有什么区别呢?

4个回答

74

您可以根据拥有的信息使用不同方法。

如果您拥有实例和静态类型并想进行检查,请使用is

如果您没有静态类型,但只有一个Type对象,并且您想要检查一个实例,请使用IsInstanceOfType

如果您没有实例,只是想检查一个理论上的Type实例与另一个Type之间的兼容性,请使用IsAssignableFrom

但实际上,这似乎就像是重新实现as运算符(除了您的方法也适用于非可空值类型,通常不会有太大限制)。


4
IsAssignableFrom 和 IsInstanceOfType 之间还有一个额外的区别。有些类型,在类型 A 可以分配给类型 B 的情况下,尽管类型 B 不是类型 A 的实例,但仍然可以分配成功。例如:class B { } class D : B { } interface IGeneric<out T> { }typeof(IGeneric<B>).IsAssignableFrom(typeof(IGeneric<D>); <== 这个表达式为真 typeof(IGeneric<D>).IsInstanceOfType(typeof(IGeneric<B>); <== 这个表达式为假 - Daniel Arant

10

我猜你正在有效地实现一个版本的as操作符,它可以处理值类型和引用类型。

我的建议是:

public static T SafeCastAs<T>(this object obj)
{
    return (obj is T) ? (T) obj : default(T);
}

IsAssignableFrom适用于类型,而is适用于实例。在您的情况下,它们将给出相同的结果,因此我认为应该使用最简单的版本。

至于IsInstanceOfType:它是通过IsAssignableFrom实现的,因此不会有任何区别。

您可以通过使用Reflector查看IsInstanceOfType()的定义来证明这一点:

public virtual bool IsInstanceOfType(object o)
{
    if (o == null)
    {
        return false;
    }
    return this.IsAssignableFrom(o.GetType());
}

3
我猜你应该选择使用 "as" 而不是你自己的 "SafeCastAs"。但是这仅适用于类(而不是结构体),因此如果您还想将此方法用于结构体,我可以帮忙。
操作符 "is" 基本上给您提供了与 Type.IsAssignableFrom 相同的效果,所以您只需要保留 "is",它检查是否能够安全地将 obj 强制转换为 T,而不会出现异常。因此,它将涵盖您方法中的两个先前检查。但是,您应该知道它不会检查是否可以将 obj 分配给 T,因为存在用户定义的转换: explicitimplicit 关键字。

我不确定你在这里的断言是正确的。typeof(int)返回一个Type类型的对象,因此第一条语句将失败,因为该Type对象显然不是int。但是,如果您将第一条语句更改为a.GetType().IsInstanceOfType(3),它将返回true。我认为您在这里使用了IsInstanceOfType错误。 - Jeff B
@JeffBridgman 是的,我的猜测是我只是试图展示使用这两种方法的区别,就像这样:Console.WriteLine(typeof(int?).IsInstanceOfType(null)); Console.WriteLine(typeof(int?).IsInstanceOfType(1)); Console.WriteLine(typeof(int?).IsAssignableFrom(typeof(int)));我将删除此答案的某些部分。 - outcoldman

1
这些函数和运算符具有不同的含义。如果您有对象,您总是可以获取类型。因此,您不是在处理您拥有的内容,而是在执行需要完成的操作。
当您使用类层次结构时,差异非常明显。
请看以下示例。
      class ABase
        {

        }

        class BSubclass : ABase
        {

        }
    ABase aBaseObj = new ABase();
                BSubclass bSubclassObj = new BSubclass();

                ABase subObjInBaseRef = new BSubclass();

不同的操作会产生不同的结果。
typeof(ABase).IsInstanceOfType(aBaseObj) = True

typeof(ABase).IsInstanceOfType(bSubclassObj) = True

typeof(ABase).IsInstanceOfType(bSubclassObj) = True

typeof(BSubclass).IsInstanceOfType(aBaseObj) = False

bSubclassObj is ABase = True

aBaseObj is BSubclass = False

subObjInBaseRef is BSubclass = True

subObjInBaseRef is BSubclass = True

typeof(ABase).IsAssignableFrom(typeof(BSubclass))  = True

typeof(BSubclass).IsAssignableFrom(typeof(ABase))= False

如果没有层次结构,可能一切都是相同的。但是如果你使用层次结构,IsAssignableFromisIsInstanceOfType会产生不同的结果。
还有更多可能尝试的组合。例如,您可以引入一个与此示例中现有类别无关的类C。

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