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

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

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

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

вопрос задан 11.06.2009
jasonh
14009 репутация

14 ответов


  • 1525 рейтинг

    Все разные.

    • 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 parameter) { return typeof(T).Name; }
    
    Animal probably_a_dog = new Dog();
    Dog    definitely_a_dog = new Dog();
    
    Foo(probably_a_dog); // this calls Foo and returns "Animal"
    Foo(probably_a_dog); // this is exactly the same as above
    Foo(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 and returns "Dog"
    Foo(definitely_a_dog); // this is exactly the same as above.
    Foo(definitely_a_dog); // this calls Foo and returns "Animal". 
    Foo((Animal)definitely_a_dog); // this does the same as above, returns "Animal"
    
    ответ дан Jimmy, с репутацией 63541, 11.06.2009
  • 166 рейтинг

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

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

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

    При этом используется только один бросок , тогда как этот подход:

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

    требует два .

    ответ дан Andrew Hare, с репутацией 268434, 11.06.2009
  • 61 рейтинг

    1.

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

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

    2.

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

    Это верно, если obj1 точно типа int. Если obj1 наследуется от int, условие if будет ложным.

    3.

    if (obj1 is int)
    

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

    ответ дан Scott Langham, с репутацией 36942, 11.06.2009
  • 40 рейтинг
    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 - Dog, а не Animal. Однако эту работу можно выполнить, если использовать метод IsAssignableFrom класса Type.

    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 будет , всегда будет Animal, и, пока их допущения верны, все работает нормально. Но если они не правы, то в итоге они получают NullReferenceException. С обычным броском они получили бы InvalidCastException вместо этого, который более правильно определил бы проблему.

    Иногда эту ошибку бывает трудно найти:

    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.

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

    ответ дан P Daddy, с репутацией 22734, 11.06.2009
  • 11 рейтинг

    У меня было свойство 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
    
    ответ дан Yahoo Serious, с репутацией 2217, 15.05.2012
  • 7 рейтинг

    Я предпочитаю это

    Тем не менее, если вы используете - это , вы, скорее всего, , а не , если используете наследование должным образом.

    Предположим, что Персона: Сущность, а это Животное: Сущность. 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
        }
      }
    }
    

    Скорее

    class Person
    {
      public override void Feed( Person p )
      {
        // feed the person
      }
      public override void Feed( Animal a )
      {
        // feed the animal
      }
    }
    
    ответ дан bobobobo, с репутацией 34399, 11.06.2009
  • 7 рейтинг

    Если вы используете 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...
    }
    

    РЕДАКТИРОВАТЬ: Обновлен более длинный новый метод для использования переключателя в соответствии с комментарием Палека.

    ответ дан JoelC, с репутацией 2243, 1.02.2018
  • 5 рейтинг

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

    ответ дан StriplingWarrior, с репутацией 105216, 11.06.2009
  • 2 рейтинг

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

    ответ дан AllenG, с репутацией 7377, 11.06.2009
  • 0 рейтинг

    Используется для получения Системы. Тип объекта для типа. Выражение 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
        */
    
    ответ дан Muhammad Awais, с репутацией 1659, 14.04.2016
  • 0 рейтинг

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

    ответ дан thecoop, с репутацией 34792, 11.06.2009
  • -3 рейтинг
    if (c is UserControl) c.Enabled = enable;
    
    ответ дан Paulos02, с репутацией 126, 12.08.2016
  • -5 рейтинг

    Вы можете использовать оператор typeof () в C #, но вам нужно вызвать пространство имен с помощью System. IO; Вы должны использовать ключевое слово «is», если хотите проверить тип.

    ответ дан androidrill, с репутацией 56, 3.04.2014
  • -5 рейтинг

    Тест производительности 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 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
    
    ответ дан Alexander Vasilyev, с репутацией 1077, 3.08.2015