简体   繁体   English

类型检查:typeof、GetType 还是 is?

[英]Type Checking: typeof, GetType, or is?

I've seen many people use the following code:我见过很多人使用以下代码:

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

But I know you could also do this:但我知道你也可以这样做:

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

Or this:或这个:

if (obj1 is int)
    // Some code here

Personally, I feel the last one is the cleanest, but is there something I'm missing?就个人而言,我觉得最后一个是最干净的,但是我缺少什么吗? Which one is the best to use, or is it personal preference?哪个最好用,还是个人喜好?

All are different.都是不同的。

  • typeof takes a type name (which you specify at compile time). typeof采用类型名称(您在编译时指定)。
  • GetType gets the runtime type of an instance. GetType获取实例的运行时类型。
  • is returns true if an instance is in the inheritance tree.如果实例在继承树中, is返回 true。

Example例子

class Animal { } 
class Dog : Animal { }

void PrintTypes(Animal a) { 
    Console.WriteLine(a.GetType() == typeof(Animal)); // false 
    Console.WriteLine(a is Animal);                   // true 
    Console.WriteLine(a.GetType() == typeof(Dog));    // true
    Console.WriteLine(a is Dog);                      // true 
}

Dog spot = new Dog(); 
PrintTypes(spot);

What about typeof(T) ?那么typeof(T)呢? Is it also resolved at compile time?它是否也在编译时解决?

Yes.是的。 T is always what the type of the expression is. T 始终是表达式的类型。 Remember, a generic method is basically a whole bunch of methods with the appropriate type.请记住,泛型方法基本上是一堆具有适当类型的方法。 Example:例子:

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 above
Foo<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"

Use typeof when you want to get the type at compilation time .如果您想在编译时获取类型,请使用typeof Use GetType when you want to get the type at execution time .如果您想在执行时获取类型,请使用GetType There are rarely any cases to use is as it does a cast and, in most cases, you end up casting the variable anyway.很少有任何情况可以使用is因为它会进行强制转换,并且在大多数情况下,您最终还是会强制转换变量。

There is a fourth option that you haven't considered (especially if you are going to cast an object to the type you find as well);您还没有考虑第四个选项(特别是如果您要将对象也转换为您找到的类型); that is to use as .那就是使用as

Foo foo = obj as Foo;

if (foo != null)
    // your code here

This only uses one cast whereas this approach:这仅使用一个演员,而这种方法:

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

requires two .需要两个

Update (Jan 2020):更新(2020 年 1 月):

  • As of C# 7+ , you can now cast inline, so the 'is' approach can now be done in one cast as well. 从 C# 7+开始,您现在可以进行内联转换,因此“is”方法现在也可以在一次转换中完成。

Example:例子:

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

1. 1.

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

This is illegal, because typeof only works on types, not on variables.这是非法的,因为typeof仅适用于类型,而不适用于变量。 I assume obj1 is a variable.我假设 obj1 是一个变量。 So, in this way typeof is static, and does its work at compile time instead of runtime.因此,以这种方式typeof是静态的,并且在编译时而不是运行时完成其工作。

2. 2.

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

This is true if obj1 is exactly of type int .如果obj1恰好是int类型,则这是true的。 If obj1 derives from int , the if condition will be false .如果obj1派生自int ,则 if 条件将为false

3. 3.

if (obj1 is int)

This is true if obj1 is an int , or if it derives from a class called int , or if it implements an interface called int . true obj1是一个int ,或者如果它派生自一个名为int的类,或者如果它实现了一个名为int的接口,则这是正确的。

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

This is an error.这是一个错误。 The typeof operator in C# can only take type names, not objects. C# 中的 typeof 运算符只能采用类型名称,不能采用对象。

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

This will work, but maybe not as you would expect.这会起作用,但可能不会像您期望的那样。 For value types, as you've shown here, it's acceptable, but for reference types, it would only return true if the type was the exact same type, not something else in the inheritance hierarchy.对于值类型,正如您在此处显示的那样,它是可以接受的,但对于引用类型,它只会在类型完全相同的情况下返回 true,而不是继承层次结构中的其他类型。 For instance:例如:

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");
}

This would print "o is something else" , because the type of o is Dog , not Animal .这将打印"o is something else" ,因为o的类型是Dog ,而不是Animal You can make this work, however, if you use the IsAssignableFrom method of the Type class.但是,如果您使用Type类的IsAssignableFrom方法,您可以完成这项工作。

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

This technique still leaves a major problem, though.但是,这种技术仍然存在一个主要问题。 If your variable is null, the call to GetType() will throw a NullReferenceException.如果您的变量为 null,则对GetType()的调用将引发 NullReferenceException。 So to make it work correctly, you'd do:因此,要使其正常工作,您可以:

if(o != null && typeof(Animal).IsAssignableFrom(o.GetType()))
    Console.WriteLine("o is an animal");

With this, you have equivalent behavior of the is keyword.有了这个,你就有了is关键字的等效行为。 Hence, if this is the behavior you want, you should use the is keyword, which is more readable and more efficient.因此,如果这是您想要的行为,您应该使用is关键字,它更具可读性和效率。

if(o is Animal)
    Console.WriteLine("o is an animal");

In most cases, though, the is keyword still isn't what you really want, because it's usually not enough just to know that an object is of a certain type.但是,在大多数情况下, is关键字仍然不是您真正想要的,因为仅仅知道对象属于某种类型通常是不够的。 Usually, you want to actually use that object as an instance of that type, which requires casting it too.通常,您希望将该对象实际用作该类型的实例,这也需要强制转换它。 And so you may find yourself writing code like this:所以你可能会发现自己在编写这样的代码:

if(o is Animal)
    ((Animal)o).Speak();

But that makes the CLR check the object's type up to two times.但这会使 CLR 最多检查对象的类型两次。 It will check it once to satisfy the is operator, and if o is indeed an Animal , we make it check again to validate the cast.它将检查一次以满足is运算符,如果o确实是Animal ,我们再次检查以验证强制转换。

It's more efficient to do this instead:这样做更有效:

Animal a = o as Animal;
if(a != null)
    a.Speak();

The as operator is a cast that won't throw an exception if it fails, instead returning null . as运算符是一个强制转换,如果失败则不会抛出异常,而是返回null This way, the CLR checks the object's type just once, and after that, we just need to do a null check, which is more efficient.这样,CLR 只检查一次对象的类型,之后我们只需要进行一次空值检查,这样效率更高。

But beware: many people fall into a trap with as .但请注意:很多人都陷入了as的陷阱。 Because it doesn't throw exceptions, some people think of it as a "safe" cast, and they use it exclusively, shunning regular casts.因为它不会抛出异常,所以有些人认为它是一个“安全”的强制转换,他们只使用它,避开常规强制转换。 This leads to errors like this:这会导致如下错误:

(o as Animal).Speak();

In this case, the developer is clearly assuming that o will always be an Animal , and as long as their assumption is correct, everything works fine.在这种情况下,开发人员显然假设o始终Animal ,并且只要他们的假设正确,一切正常。 But if they're wrong, then what they end up with here is a NullReferenceException .但是如果他们错了,那么他们最终会得到一个NullReferenceException With a regular cast, they would have gotten an InvalidCastException instead, which would have more correctly identified the problem.使用常规强制转换,他们会得到一个InvalidCastException ,这会更正确地识别问题。

Sometimes, this bug can be hard to find:有时,这个错误很难找到:

class Foo{
    readonly Animal animal;

    public Foo(object o){
        animal = o as Animal;
    }

    public void Interact(){
        animal.Speak();
    }
}

This is another case where the developer is clearly expecting o to be an Animal every time, but this isn't obvious in the constructor, where the as cast is used.这是另一种情况,开发人员显然希望o每次都是Animal ,但这在使用as强制转换的构造函数中并不明显。 It's not obvious until you get to the Interact method, where the animal field is expected to be positively assigned.在您使用Interact方法之前,这并不明显,其中animal字段预计会被积极分配。 In this case, not only do you end up with a misleading exception, but it isn't thrown until potentially much later than when the actual error occurred.在这种情况下,您不仅最终会得到一个误导性的异常,而且直到可能比实际错误发生的时间晚得多时才会抛出它。

In summary:总之:

  • If you only need to know whether or not an object is of some type, use is .如果您只需要知道对象是否属于某种类型,请使用is

  • If you need to treat an object as an instance of a certain type, but you don't know for sure that the object will be of that type, use as and check for null .如果您需要将对象视为某种类型的实例,但您不确定该对象是否属于该类型,请使用as并检查null

  • If you need to treat an object as an instance of a certain type, and the object is supposed to be of that type, use a regular cast.如果您需要将对象视为某种类型的实例,并且该对象应该属于该类型,请使用常规强制转换。

If you're using C# 7, then it is time for an update to Andrew Hare's great answer.如果您使用的是 C# 7,那么是时候更新 Andrew Hare 的出色答案了。 Pattern matching has introduced a nice shortcut that gives us a typed variable within the context of the if statement, without requiring a separate declaration/cast and check: 模式匹配引入了一个很好的快捷方式,它在 if 语句的上下文中为我们提供了一个类型化变量,而无需单独的声明/强制转换和检查:

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

This looks pretty underwhelming for a single cast like this, but really shines when you have many possible types coming into your routine.对于像这样的单个演员来说,这看起来相当平淡无奇,但当你有许多可能的类型进入你的日常工作时,它真的会发光。 The below is the old way to avoid casting twice:以下是避免两次投射的旧方法:

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

Working around shrinking this code as much as possible, as well as avoiding duplicate casts of the same object has always bothered me.尽可能地缩小这段代码,以及避免对同一对象的重复转换一直困扰着我。 The above is nicely compressed with pattern matching to the following:以上内容通过模式匹配很好地压缩为以下内容:

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

EDIT: Updated the longer new method to use a switch as per Palec's comment.编辑:根据 Palec 的评论更新了更长的新方法以使用开关。

I had a Type -property to compare to and could not use is (like my_type is _BaseTypetoLookFor ), but I could use these:我有一个Type -property 可以比较并且不能使用is (比如my_type is _BaseTypetoLookFor ),但我可以使用这些:

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

Notice that IsInstanceOfType and IsAssignableFrom return true when comparing the same types, where IsSubClassOf will return false .请注意,比较相同类型时IsInstanceOfTypeIsAssignableFrom返回true ,其中 IsSubClassOf 将返回false And IsSubclassOf does not work on interfaces, where the other two do.并且IsSubclassOf不适用于其他两个接口的接口。 (See also this question and answer .) (另请参阅此问题和答案。)

public class Animal {}
public interface ITrainable {}
public class Dog : Animal, ITrainable{}

Animal dog = new Dog();

typeof(Animal).IsInstanceOfType(dog);     // true
typeof(Dog).IsInstanceOfType(dog);        // true
typeof(ITrainable).IsInstanceOfType(dog); // true

typeof(Animal).IsAssignableFrom(dog.GetType());      // true
typeof(Dog).IsAssignableFrom(dog.GetType());         // true
typeof(ITrainable).IsAssignableFrom(dog.GetType()); // true

dog.GetType().IsSubclassOf(typeof(Animal));            // true
dog.GetType().IsSubclassOf(typeof(Dog));               // false
dog.GetType().IsSubclassOf(typeof(ITrainable)); // false

I prefer is我更喜欢的是

That said, if you're using is , you're likely not using inheritance properly.也就是说,如果您使用的是 is ,您可能没有正确使用继承。

Assume that Person : Entity, and that Animal : Entity.假设 Person : Entity 和 Animal : Entity。 Feed is a virtual method in Entity (to make Neil happy) Feed 是 Entity 中的一个虚拟方法(让 Neil 开心)

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

Rather相当

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

我相信最后一个也关注继承(例如 Dog is Animal == true),这在大多数情况下更好。

It depends on what I'm doing.这取决于我在做什么。 If I need a bool value (say, to determine if I'll cast to an int), I'll use is .如果我需要一个 bool 值(例如,确定是否将转换为 int),我将使用is If I actually need the type for some reason (say, to pass to some other method) I'll use GetType() .如果出于某种原因我确实需要该类型(例如,传递给其他方法),我将使用GetType()

Used to obtain the System.Type object for a type.用于获取类型的 System.Type 对象。 A typeof expression takes the following form: 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
    */

This sample uses the GetType method to determine the type that is used to contain the result of a numeric calculation.此示例使用 GetType 方法来确定用于包含数值计算结果的类型。 This depends on the storage requirements of the resulting number.这取决于结果数字的存储要求。

    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.2743338823081
    The type is System.Double
    */

I found checking if the type of something is equal to something is done by the following:我发现检查某物的类型是否等于某物是通过以下方式完成的:

variableName.GetType() == typeof(int)

The last one is cleaner, more obvious, and also checks for subtypes.最后一个更清晰,更明显,并且还检查子类型。 The others do not check for polymorphism.其他的不检查多态性。

if (c is UserControl) c.Enabled = enable;

You can use "typeof()" operator in C# but you need to call the namespace using System.IO;您可以在 C# 中使用“typeof()”运算符,但需要使用 System.IO 调用命名空间; You must use "is" keyword if you wish to check for a type.如果要检查类型,则必须使用“is”关键字。

Performance test typeof() vs GetType():性能测试 typeof() 与 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();
    }
}

Results in debug mode:调试模式下的结果:

00:00:08.4096636
00:00:10.8570657

Results in release mode:发布模式的结果:

00:00:02.3799048
00:00:07.1797128

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 GetType和typeof - GetType and typeof typeof和GetType - typeof & GetType typeof或Type.GetType用于非限定类型名称 - typeof or Type.GetType for a non qualified type name typeof(Foo)有效,但Type.GetType(“ Foo”)为null - typeof(Foo) works but Type.GetType(“Foo”) is null C#中的GetType()和Typeof() - GetType() and Typeof() in C# GetType()和typeof()是否构成反射? - Does GetType() and typeof() constitute reflection? c#测试一个对象是否实现了ISurface <T> 任何类型使用.GetType()。GetInterface(typeof(ISurface &lt;&gt;)。FullName) - c# testing if an object implements ISurface<T> of any Type using .GetType().GetInterface(typeof(ISurface<>).FullName) 如何在IronPython中检查类型相等性(运算符或x.GetType()== typeof(xType))? - How do I check for type equality (is operator or x.GetType() == typeof(xType)) in IronPython? typeof(String)与System.Type.GetType(“System.String”)的性能 - Performance of typeof(String) vs System.Type.GetType(“System.String”) 如果不需要强制转换,并且需要检查对象的最具体(运行时)类型,哪个更快? .GetType() &amp; typeof(),还是算符? - If you need no casting, and you need to check the most specific (runtime) type of an object, which is faster? .GetType() & typeof(), or is operator?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM