提问



我见过很多人使用以下代码:


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


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


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


或这个:


if (obj1 is int)
    // Some code here


就我个人而言,我觉得最后一个是最干净的,但是我有什么遗漏?哪一个最好用,还是个人偏好?

最佳参考


一切都不一样。



  • typeof采用类型名称(您在编译时指定)。

  • GetType获取实例的运行时类型。

  • 如果实例在继承树中,则
  • is返回true。



实施例



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






  那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 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"

其它参考1


如果要在编译时获取类型,请使用typeof。如果要在执行时间获取类型,请使用GetType。很少有任何案例可以使用is,因为它会进行演员表演,而且在大多数情况下,你最终还是会投射变量。


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


Foo foo = obj as Foo;

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


这仅使用一个演员而这种方法:


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


需要两个

其它参考2


1


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


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


2


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


如果obj1完全是int类型,则为true。如果obj1派生自int,则if条件将为false。


第3


if (obj1 is int)


如果obj1是一个int,或者它派生自一个名为int的类,或者它实现了一个名为int的接口,那么这是正确的。

其它参考3


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 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,这样可以更准确地识别问题。


有时,这个bug很难找到:


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

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


其它参考4


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


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


请注意,IsInstanceOfTypeIsAssignableFrom在比较相同类型时返回true,其中IsSubClassOf将返回false。并且IsSubclassOf不适用于其他两个接口的接口。 (另见这个问题和答案。)


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

其它参考5


我更喜欢


也就是说,如果您正在使用 ,那么您很可能 正确使用继承。


假设Person:Entity和那个Animal:Entity。 Feed是实体中的虚拟方法(让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
    }
  }
}





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

其它参考6


如果您正在使用C#7,那么现在是时候更新Andrew Hare了。模式匹配引入了一个很好的快捷方式,它在if语句的上下文中提供了一个类型变量,而不需要单独的声明/强制转换和检查:[81]


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的评论更新了使用开关的更长的新方法。

其它参考7


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

其它参考8


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

其它参考9


最后一个是更清除,更明显,并检查子类型。其他人不检查多态性。

其它参考10


用于获取类型的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.2743338823081
    The type is System.Double
    */

其它参考11


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

其它参考12


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

其它参考13


性能测试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.4096636
00:00:10.8570657


发布模式中的结果:


00:00:02.3799048
00:00:07.1797128