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是其他东西”,因为o的类型是狗,而不是动物。但是,如果使用Type类的IsAssignableFrom方法,则可以实现这一点。
if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type
Console.WriteLine("o is an animal");
然而,这一技术仍然存在一个主要问题。如果变量为空,则对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永远是动物,只要他们的假设是正确的,一切都正常。但如果他们错了,那么他们最终会得到一个NullReferenceException。如果使用常规强制转换,则会得到InvalidCastException,这将更正确地识别问题。
有时,这个bug很难找到:
class Foo{
readonly Animal animal;
public Foo(object o){
animal = o as Animal;
}
public void Interact(){
animal.Speak();
}
}
这是另一种情况,开发人员显然希望o每次都是Animal,但这在构造函数中并不明显,在构造函数中使用了as cast。在使用Interactive方法之前,这是不明显的,在该方法中,动物场将被正向分配。在这种情况下,不仅会导致一个误导性的异常,而且在可能比实际错误发生的时间晚得多的时候才会引发异常。
总而言之:
如果您只需要知道一个对象是否属于某种类型,请使用。如果您需要将对象视为某一类型的实例,但不确定该对象是否为该类型,请使用as并检查是否为null。如果需要将对象视为某个类型的实例,并且该对象应该是该类型的,请使用常规强制转换。