类型检查:typeof, GetType, or is?

我看到很多人使用以下代码:

Type t = obj1.GetType();if (t == typeof(int))// Some code here

但我知道你也可以这样做:

if (obj1.GetType() == typeof(int))// Some code here

或者这个:

if (obj1 is int)// Some code here

就我个人而言,我觉得最后一个是最干净的,但是我是否错过了什么?哪一个是最好的,还是个人偏好?

1331838 次浏览

我相信最后一个也看继承(例如,狗是动物==true),这在大多数情况下更好。

这取决于我在做什么。如果我需要一个bool值(例如,确定我是否要转换为int),我会使用is。如果我出于某种原因确实需要该类型(例如,传递给其他方法),我会使用GetType()

当您想在编译时间获取类型时使用typeof。当您想在执行时间获取类型时使用GetType。很少有任何情况可以使用is,因为它会进行转换,在大多数情况下,您最终还是会转换变量。

还有第四个选项你没有考虑过(特别是如果你要将对象强制转换为你找到的类型);那就是使用as

Foo foo = obj as Foo;
if (foo != null)// your code here

这只使用一个 cast,而这种方法:

if (obj is Foo)Foo foo = (Foo)obj;

需要两个.

更新(2020年1月):

  • 从C#7+开始,您现在可以内联转换,因此'is'方法现在也可以在一次转换中完成。

示例:

if(obj is Foo newLocalFoo){// For example, you can now reference 'newLocalFoo' in this local scopeConsole.WriteLine(newLocalFoo);}

我更喜欢

也就是说,如果你使用的是,你很可能是没有正确地使用了继承。

假设Person: Entity,动物: Entity。Feed是Entity中的一个虚拟方法(让Neil开心)

class Person{// A Person should be able to Feed// another Entity, but they way he feeds// each is differentpublic override void Feed( Entity e ){if( e is Person ){// feed me}else if( e is Animal ){// ruff}}}

而是

class Person{public override void Feed( Person p ){// feed the person}public override void Feed( Animal a ){// feed the animal}}

每个人都不一样。

  • typeof采用类型名称(您在编译时指定)。
  • GetType获取实例的运行时类型。
  • 如果实例在继承树中,则is返回true。

示例

class Animal { }class Dog : Animal { }
void PrintTypes(Animal a) {Console.WriteLine(a.GetType() == typeof(Animal)); // falseConsole.WriteLine(a is Animal);                   // trueConsole.WriteLine(a.GetType() == typeof(Dog));    // trueConsole.WriteLine(a is Dog);                      // true}
Dog spot = new Dog();PrintTypes(spot);

typeof(T)呢?它也在编译时解决吗?

是的。T总是表达式的类型。记住,泛型方法基本上是一大堆具有适当类型的方法。例子:

string Foo<T>(T parameter) { return typeof(T).Name; }
Animal probably_a_dog = new Dog();Dog    definitely_a_dog = new Dog();
Foo(probably_a_dog); // this calls Foo<Animal> and returns "Animal"Foo<Animal>(probably_a_dog); // this is exactly the same as aboveFoo<Dog>(probably_a_dog); // !!! This will not compile. The parameter expects a Dog, you cannot pass in an Animal.
Foo(definitely_a_dog); // this calls Foo<Dog> and returns "Dog"Foo<Dog>(definitely_a_dog); // this is exactly the same as above.Foo<Animal>(definitely_a_dog); // this calls Foo<Animal> and returns "Animal".Foo((Animal)definitely_a_dog); // this does the same as above, returns "Animal"

最后一个更清晰,更明显,也检查亚型。其他不检查多态性。

1.

Type t = typeof(obj1);if (t == typeof(int))

这是非法的,因为typeof只适用于类型,而不适用于变量。我假设Objectiv1是一个变量。因此,通过这种方式,typeof是静态的,并且在编译时而不是运行时工作。

2.

if (obj1.GetType() == typeof(int))

如果obj1正好是int的类型,则为true。如果obj1派生自int,则if条件为false

3.

if (obj1 is int)

如果obj1int,或者如果它派生自名为int的类,或者如果它实现了名为int的接口,则为true

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。但是,如果您使用Type类的IsAssignableFrom方法,您可以使此工作。

if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested typeConsole.WriteLine("o is an animal");

不过,这种技术仍然存在一个主要问题。如果你的变量为空,对GetType()的调用将抛出一个NullRe的异常。因此,为了使其正常工作,你需要做:

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,这会更正确地识别问题。

有时,这种bug很难找到:

class Foo{readonly Animal animal;
public Foo(object o){animal = o as Animal;}
public void Interact(){animal.Speak();}}

这是另一种情况,开发人员每次都明显期望oAnimal,但这在使用as转换的构造函数中并不明显。直到你到达Interact方法,其中animal字段预计被正分配,这才明显。在这种情况下,你不仅会得到一个误导性的异常,而且直到可能比实际错误发生的时候晚得多才抛出它。

总结:

  • 如果您只需要知道对象是否属于某种类型,请使用is

  • 如果您需要将对象视为某种类型的实例,但您不确定该对象是否属于该类型,请使用as并检查null

  • 如果您需要将对象视为某种类型的实例,并且该对象应该属于该类型,请使用常规强制转换。

我有一个Type属性可以比较,不能使用is(如my_type is _BaseTypetoLookFor),但我可以使用这些:

base_type.IsInstanceOfType(derived_object);base_type.IsAssignableFrom(derived_type);derived_type.IsSubClassOf(base_type);

请注意,在比较相同类型时,IsInstanceOfTypeIsAssignableFrom返回true,其中IsSubClassOf将返回falseIsSubclassOf在接口上不起作用,其他两个在接口上起作用。(另见这个问题和答案。)

public class Animal {}public interface ITrainable {}public class Dog : Animal, ITrainable{}
Animal dog = new Dog();
typeof(Animal).IsInstanceOfType(dog);     // truetypeof(Dog).IsInstanceOfType(dog);        // truetypeof(ITrainable).IsInstanceOfType(dog); // true
typeof(Animal).IsAssignableFrom(dog.GetType());      // truetypeof(Dog).IsAssignableFrom(dog.GetType());         // truetypeof(ITrainable).IsAssignableFrom(dog.GetType()); // true
dog.GetType().IsSubclassOf(typeof(Animal));            // truedog.GetType().IsSubclassOf(typeof(Dog));               // falsedog.GetType().IsSubclassOf(typeof(ITrainable)); // false

您可以在C#中使用“typeof()”运算符,但需要使用System.IO调用命名空间;如果要检查类型,必须使用“is”关键字。

性能测试typeof()vs GetType():

using System;namespace ConsoleApplication1{class Program{enum TestEnum { E1, E2, E3 }static void Main(string[] args)\{\{var start = DateTime.UtcNow;for (var i = 0; i < 1000000000; i++)Test1(TestEnum.E2);Console.WriteLine(DateTime.UtcNow - start);}{var start = DateTime.UtcNow;for (var i = 0; i < 1000000000; i++)Test2(TestEnum.E2);Console.WriteLine(DateTime.UtcNow - start);}Console.ReadLine();}static Type Test1<T>(T value) => typeof(T);static Type Test2(object value) => value.GetType();}}

调试模式下的结果:

00:00:08.409663600:00:10.8570657

释放模式下的结果:

00:00:02.379904800:00:07.1797128

用于获取类型的System. Type对象。typeof表达式采用以下形式:

System.Type type = typeof(int);
Example:
public class ExampleClass{public int sampleMember;public void SampleMethod() {}
static void Main(){Type t = typeof(ExampleClass);// Alternatively, you could use// ExampleClass obj = new ExampleClass();// Type t = obj.GetType();
Console.WriteLine("Methods:");System.Reflection.MethodInfo[] methodInfo = t.GetMethods();
foreach (System.Reflection.MethodInfo mInfo in methodInfo)Console.WriteLine(mInfo.ToString());
Console.WriteLine("Members:");System.Reflection.MemberInfo[] memberInfo = t.GetMembers();
foreach (System.Reflection.MemberInfo mInfo in memberInfo)Console.WriteLine(mInfo.ToString());}}/*Output:Methods:Void SampleMethod()System.String ToString()Boolean Equals(System.Object)Int32 GetHashCode()System.Type GetType()Members:Void SampleMethod()System.String ToString()Boolean Equals(System.Object)Int32 GetHashCode()System.Type GetType()Void .ctor()Int32 sampleMember*/

此示例使用GetType方法来确定用于包含数字计算结果的类型。这取决于结果数字的存储要求。

    class GetTypeTest{static void Main(){int radius = 3;Console.WriteLine("Area = {0}", radius * radius * Math.PI);Console.WriteLine("The type is {0}",(radius * radius * Math.PI).GetType());}}/*Output:Area = 28.2743338823081The type is System.Double*/
if (c is UserControl) c.Enabled = enable;

模式匹配引入了一个不错的快捷方式,它在if语句的上下文中为我们提供了一个类型化变量,而不需要单独的声明/转换和检查:

if (obj1 is int integerValue){integerValue++;}

对于这样的单个演员来说,这看起来很平淡无奇,但当你有许多可能的类型进入你的例程时,这真的很出色。

Button button = obj1 as Button;if (button != null){// do stuff...return;}TextBox text = obj1 as TextBox;if (text != null){// do stuff...return;}Label label = obj1 as Label;if (label != null){// do stuff...return;}// ... and so on

尽可能缩小这段代码,以及避免相同对象的重复转换一直困扰着我。上面的内容通过模式匹配很好地压缩到了以下内容:

switch (obj1){case Button button:// do stuff...break;case TextBox text:// do stuff...break;case Label label:// do stuff...break;// and so on...}

编辑:根据Palec的评论更新了使用开关的更长的新方法。

我发现检查某物的类型是否等于某物是通过以下方式完成的:

variableName.GetType() == typeof(int)