Type t = typeof(obj1);
if (t == typeof(int))
// Some code here
这是一个错误。C# 中的 typeof 运算符只能采用类型名称,不能采用对象。
if (obj1.GetType() == typeof(int))
// Some code here
这会起作用,但可能不会像您期望的那样。对于值类型,正如您在此处显示的那样,它是可以接受的,但对于引用类型,它只会在类型是完全相同的类型时返回 true ,而不是在继承层次结构中的其他类型。例如:
class Animal{}
class Dog : Animal{}
static void Foo(){
object o = new Dog();
if(o.GetType() == typeof(Animal))
Console.WriteLine("o is an animal");
Console.WriteLine("o is something else");
}
这将打印"o is something else"
,因为类型o
是Dog
,不是Animal
。但是,如果您使用该类的IsAssignableFrom
方法,您可以完成这项工作。Type
if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type
Console.WriteLine("o is an animal");
但是,这种技术仍然存在一个主要问题。如果您的变量为 null,则调用GetType()
将引发 NullReferenceException。因此,要使其正常工作,您可以:
if(o != null && typeof(Animal).IsAssignableFrom(o.GetType()))
Console.WriteLine("o is an animal");
有了这个,你就有了is
关键字的等效行为。因此,如果这是您想要的行为,您应该使用is
更易读、更高效的关键字。
if(o is Animal)
Console.WriteLine("o is an animal");
但是,在大多数情况下,is
关键字仍然不是您真正想要的,因为仅仅知道对象属于某种类型通常是不够的。通常,您希望将该对象实际用作该类型的实例,这也需要强制转换它。所以你可能会发现自己在编写这样的代码:
if(o is Animal)
((Animal)o).Speak();
但这会使 CLR 最多检查对象的类型两次。它将检查一次以满足is
操作员的要求,如果o
确实是一个Animal
,我们让它再次检查以验证强制转换。
这样做更有效:
Animal a = o as Animal;
if(a != null)
a.Speak();
运算符是一个强制转换,as
如果失败则不会抛出异常,而是返回null
. 这样,CLR 只检查一次对象的类型,之后我们只需要进行一次空值检查,这样效率更高。
但请注意:很多人都掉入了as
. 因为它不会抛出异常,所以有些人认为它是一个“安全”的强制转换,他们只使用它,避开常规强制转换。这会导致如下错误:
(o as Animal).Speak();
在这种情况下,开发人员显然假设它o
总是一个,只要他们的假设是正确的,一切都会正常工作。Animal
但如果他们错了,那么他们最终会得到一个NullReferenceException
. 使用常规演员,他们会得到一个InvalidCastException
替代品,这样可以更正确地识别问题。
有时,这个错误很难找到:
class Foo{
readonly Animal animal;
public Foo(object o){
animal = o as Animal;
}
public void Interact(){
animal.Speak();
}
}
这是另一种情况,开发人员显然希望每次o
都是一个,但这在使用强制转换的构造函数中并不明显。在您使用该方法之前并不明显,该方法预计该字段将被积极分配。在这种情况下,您不仅最终会得到一个误导性的异常,而且直到可能比实际错误发生的时间晚得多时才会抛出它。Animal
as
Interact
animal
总之:
如果您只需要知道对象是否属于某种类型,请使用is
.
如果您需要将对象视为某种类型的实例,但您不确定该对象是否属于该类型,请使用as
并检查null
.
如果您需要将对象视为某种类型的实例,并且该对象应该属于该类型,请使用常规强制转换。