Проверка типа: typeof, GetType или is?

Я видел, как многие люди используют следующий код:

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

лично я чувствую, что последний самый чистый, но есть ли что-то, чего мне не хватает? Какой из них лучше всего использовать, или это личное предпочтение?

14 ответов


все разные.

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

использовать typeof когда вы хотите сделать типа компиляции. Использовать GetType когда вы хотите сделать типа срок исполнения. Редко бывают случаи использования is как это делает бросок и, в большинстве случаев, вы в конечном итоге в любом случае переменная.

есть четвертый вариант, который вы не рассматривали (особенно, если вы собираетесь привести объект к типу, который вы найдете); то есть использовать as.

Foo foo = obj as Foo;

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

этот только использует один cast тогда как этот подход:

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

требует два.


1.

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

это незаконно, потому что typeof работает только с типами, а не с переменными. Я предполагаю, что obj1 является переменной. Таким образом, typeof является статическим и выполняет свою работу во время компиляции, а не во время выполнения.

2.

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

Это верно, если obj1 точно имеет тип int. Если obj1 происходит от int, условие if будет false.

3.

if (obj1 is int)

Это верно, если obj1 int, или если это происходит из класса int, или если он реализует интерфейс инт.


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

это ошибка. Оператор typeof в C# может принимать только имена типов, а не объекты.

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 is Dog, а не Animal. Вы можете сделать эту работу, однако, если вы используете IsAssignableFrom метод Type класса.

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 проверяет тип объекта только один раз, и после этого нам просто нужно выполнить проверку null, которая больше эффективный.

но будьте осторожны: многие люди попадают в ловушку с 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 используется cast. Это не очевидно, пока вы не доберетесь до Interact способ, где animal ожидается, что поле будет положительно назначено. В этом случае вы не только получаете вводящее в заблуждение исключение, но оно не выдается до потенциально намного позже, чем когда произошла фактическая ошибка.

в итоге:

  • Если вам нужно только знать, является ли объект какого-либо типа, используйте is.

  • Если вам нужно обработать объект как экземпляр определенного типа, но вы не знаете точно, что объект будет такого типа, use as и чек на null.

  • Если вам нужно обработать объект как экземпляр определенного типа, и объект должен быть того типа, использовать обычный литой.


я Type - свойство для сравнения и не может использовать is (как my_type is _BaseTypetoLookFor), но я мог бы использовать:

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

обратите внимание, что IsInstanceOfType и IsAssignableFrom возвращение 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

Если вы используете C# 7, то пришло время обновить отличный ответ Эндрю Хара. шаблоны ввел хороший ярлык, который дает нам типизированную переменную в контексте оператора 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...
}

EDIT: обновлен более длинный новый метод для использования коммутатора в соответствии с комментарием Palec.


предпочитаю is

что сказал, Если вы используете is, вы, вероятно,не использование наследования правильно.

предположим, что человек: сущность и животное : сущность. Feed-это виртуальный метод в Entity (чтобы сделать Нила счастливым)

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
  }
}

Я считаю, что последний также смотрит на наследование (например, собака-животное == true), что лучше в большинстве случаев.


Это зависит от того, что я делаю. Если мне нужно значение bool (скажем, чтобы определить, буду ли я использовать int), я буду использовать is. Если мне действительно нужен тип по какой-то причине (скажем, для перехода к другому методу), я буду использовать GetType().


последний более чистый, более очевидный, а также проверяет подтипы. Остальные не проверяют на полиморфизм.


используется для получения системы.Тип объекта для типа. Выражение 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
    */

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

вы можете использовать оператор "typeof ()" в C#, но вам нужно вызвать пространство имен с помощью 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.4096636
00:00:10.8570657

результаты в режиме релиза:

00:00:02.3799048
00:00:07.1797128