Type t = typeof(obj1);
if (t == typeof(int))
// Some code here
这是一个错误。 C# 中的 typeof 运算符只能取类型名,不能取对象。
if (obj1.GetType() == typeof(int))
// Some code here
这会起作用,但可能不像您所期望的那样。对于值类型,正如您在此处显示的那样,它是可以接受的,但对于引用类型,它只会在类型是 exact same 类型时返回 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。但是,如果您使用 Type 类的 IsAssignableFrom 方法,则可以完成这项工作。
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。
如果您需要将对象视为某种类型的实例,并且该对象应该属于该类型,请使用常规强制转换。