Проверка типа: 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

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


person jasonh    schedule 11.06.2009    source источник
comment
Не забывай as!   -  person RCIX    schedule 08.10.2009
comment
as на самом деле не проверка типов ...   -  person jasonh    schedule 08.10.2009
comment
as - это определенно форма проверки типов, каждый бит не меньше, чем is! Он эффективно использует is за кулисами и используется повсюду в MSDN, где он улучшает чистоту кода по сравнению с is. Вместо того, чтобы сначала проверять is, вызов as устанавливает типизированную переменную, готовую к использованию: если она пуста, ответьте соответствующим образом; в противном случае продолжайте. Конечно, кое-что я видел и довольно часто использовал.   -  person Zaccone    schedule 20.08.2014
comment
Существует значительная разница в производительности в пользу _1 _ / _ 2_ (описана в stackoverflow.com/a/27813381/477420) при условии, что семантика работает в вашем случае.   -  person Alexei Levenkov    schedule 07.01.2015
comment
@samusarin он не использует отражение. Метод GetType, на который вы ссылаетесь, находится в System.Reflection.Assembly - это совершенно другой метод и здесь не имеет значения.   -  person Kirk Woll    schedule 11.08.2017
comment
Я думаю, что typeof (obj1) - это синтаксическая ошибка. Я думаю, вы имели в виду Type t = obj1.GetType ();   -  person LongChalk    schedule 12.03.2019
comment
Я думаю, что RCIC имел в виду, что за проверкой типа обычно следует приведение, и в таких случаях проверка типа (т.е. есть) может быть сохранена: var castedType = otherType as CastedType; if (castedType! = null) {продолжить использование объекта типа CastedType};   -  person user3285954    schedule 15.03.2020


Ответы (14)


Все разные.

  • typeof принимает имя типа (которое вы указываете во время компиляции).
  • GetType получает тип среды выполнения экземпляра.
  • is возвращает истину, если экземпляр находится в дереве наследования.

Пример

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"
person Jimmy    schedule 11.06.2009
comment
Итак, если у меня есть класс Ford, производный от Car и экземпляр Ford, проверка того, является ли Car в этом экземпляре, будет истинной. Имеет смысл! - person jasonh; 11.06.2009
comment
Чтобы уточнить, я знал об этом, но я прокомментировал это до того, как вы добавили образец кода. Я хотел попытаться добавить ясности английского языка к вашему и без того отличному ответу. - person jasonh; 11.06.2009
comment
@Shimmy, если typeof оценивается во время компиляции, а GetType () оценивается во время выполнения, тогда имеет смысл, что GetType () вызывает небольшое снижение производительности - person Cedric Mamo; 10.12.2012
comment
как насчет new Dog (). GetType () is Animal или typeof (Dog) is Animal, он просто выдает предупреждение, а не ошибку? - person Prerak K; 07.05.2014
comment
@PrerakK new Dog().GetType() is Animal возвращает false (как и другую вашу версию), поскольку .GetType() возвращает объект типа Type, а Type не является Animal. - person Maarten; 07.11.2014
comment
В более общем смысле, когда вы используете is, если результат (true или false) известен во время компиляции, вы получаете предупреждение во время компиляции. Это означает, что вам следует изменить свой код! Пример 1: void M(Dog d) { var test = d is System.Exception; } Во время компиляции можно увидеть, что пустая ссылка или экземпляр Dog никогда не могут быть экземпляром System.Exception, потому что задействованные типы являются class типами, и каждый class может иметь только один прямой базовый класс. Пример 2: void M(int i) { var test = i is IConvertible; } Во время компиляции можно увидеть, что это всегда верно (i никогда не имеет значения NULL). - person Jeppe Stig Nielsen; 10.01.2017
comment
Я вижу, что ваш пример работает, но кажется, что он не работает, когда наследование - это объект для Int32 ... Параметр - это объект, я спрашиваю, параметр - Int32 = false, он должен быть правдой. - person Yogurtu; 09.01.2018
comment
Привет, @Yogurtu, test(Object o) { return o is Int32; } вернет истину для test(10). - person Jimmy; 09.01.2018
comment
Тема размышлений и используемых методов очень полезна и в то же время немного сложна и требует дальнейшего изучения и работы в этой области. - person elnaz jangi; 12.06.2020

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

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

Foo foo = obj as Foo;

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

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

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

требуется два.

Обновление (январь 2020 г.):

  • Начиная с C # 7+, теперь вы можете выполнять встроенное преобразование, поэтому подход« есть »теперь может быть реализован и в одном преобразовании.

Пример:

if(obj is Foo newLocalFoo)
{
    // For example, you can now reference 'newLocalFoo' in this local scope
    Console.WriteLine(newLocalFoo);
}
person Andrew Hare    schedule 11.06.2009
comment
С изменениями в .NET 4 is по-прежнему выполняет приведение? - person ahsteele; 22.03.2013
comment
Это правильный ответ? Верно ли, что вы действительно можете передать экземпляр в typeof ()? Мой опыт был отрицательным. Но я полагаю, что в целом верно, что проверка экземпляра может происходить во время выполнения, тогда как проверка класса должна выполняться во время компиляции. - person Jon Coombs; 08.10.2013
comment
@jon (через 4 года после вашего q.), нет, вы не можете передать экземпляр в typeof(), и этот ответ не предполагает, что вы можете. Вместо этого вы передаете тип, т.е. typeof(string) работает, typeof("foo") - нет. - person Abel; 20.09.2017
comment
Я не верю, что is выполняет приведение как таковое, это скорее специальная операция в IL. - person abatishchev; 22.10.2018
comment
Теперь мы можем сделать if (obj is Foo foo) { /* use foo here */ } - person Ivan García Topete; 17.04.2019
comment
Редко встречаются случаи, когда нужно использовать приведение, и в большинстве случаев вы все равно приводите переменную. - На самом деле существует вполне допустимый вариант использования is. См. Ответ JoelC. Вы можете использовать это для проверки типов и встроенного преобразования. Очень аккуратно. Конечно, я считаю, что это доступно только в C # 7 и выше. - person Colm Bhandal; 14.08.2019

1.

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

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

2.

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

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

3.

if (obj1 is int)

Это true, если obj1 является int, или если он является производным от класса с именем int, или если он реализует интерфейс с именем int.

person Scott Langham    schedule 11.06.2009
comment
Думая о 1, вы правы. И все же я видел это здесь в нескольких примерах кода. Это должно быть Type t = obj1.GetType (); - person jasonh; 11.06.2009
comment
Да, я так думаю. typeof (obj1) не компилируется, когда я пробую. - person Scott Langham; 11.06.2009
comment
Невозможно наследовать от System.Int32 или любого другого типа значения в C # - person reggaeguitar; 04.12.2014
comment
можете ли вы сказать, что будет typeof (typeof (system.int32)) - person Sana; 27.06.2018
comment
@Sana, почему бы вам не попробовать :) Я представляю, если вы вернете экземпляр System.Type, который представляет тип System.Type! Документация для typeof находится здесь: docs.microsoft.com/ en-us / dotnet / csharp / language-reference / - person Scott Langham; 02.07.2018

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

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

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

Это сработает, но, возможно, не так, как вы ожидали. Для типов значений, как вы показали здесь, это приемлемо, но для ссылочных типов оно вернет истину только в том случае, если тип будет точно таким же типом, а не чем-то другим в иерархии наследования. Например:

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.

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

person P Daddy    schedule 11.06.2009
comment
что с этим не так if (o is Animal) ((Animal) o) .Speak (); ? не могли бы вы дать более подробную информацию? - person Emil; 11.01.2017
comment
@batmaci: он в ответе - вызывает две проверки типа. Первый раз - o is Animal, который требует, чтобы среда CLR проверила, является ли тип переменной o Animal. Второй раз он проверяет, когда приводит оператор ((Animal)o).Speak(). Вместо того, чтобы проверять дважды, проверьте один раз, используя as. - person siride; 28.07.2018
comment
Я нашел это прекрасным объяснением, спасибо за разъяснения! - person Paul Efford; 29.11.2019

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

person JoelC    schedule 01.02.2018

У меня было свойство 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
person Yahoo Serious    schedule 15.05.2012

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

Тем не менее, если вы используете 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
  }
}
person bobobobo    schedule 11.06.2009
comment
Правда, я бы никогда не поступил так, зная, что Person происходит от Animal. - person jasonh; 11.06.2009
comment
Последний на самом деле тоже не использует наследование. Foo должен быть виртуальным методом Entity, который переопределяется в Person и Animal. - person Neil Williams; 11.06.2009
comment
@bobobobo Я думаю, вы имеете в виду перегрузку, а не наследование. - person lc.; 11.06.2009
comment
@lc: Нет, я имею в виду наследование. Первый пример - это своего рода неправильный способ (с использованием is) получить другое поведение. Во втором примере используется перегрузка yes, но избегается использование is. - person bobobobo; 11.06.2009
comment
Хороший момент в том, что использование ключевого слова is, как правило, является плохим выбором дизайна. Однако предоставленное решение не исправляет дизайн, а просто меняет способ его реализации. - person ebrown; 12.06.2009
comment
Проблема с примером в том, что он не масштабируется. Если вы добавили новые сущности, которые нужно было есть (например, насекомое или монстр), вам нужно будет добавить новый метод в класс Entity, а затем переопределить его в подклассах, которые будут его кормить. Это не более предпочтительно, чем список if (entity is X) else if (entity is Y) ... Это нарушает LSP и OCP, наследование, вероятно, не лучшее решение проблемы. Вероятно, будет предпочтительна какая-либо форма делегирования. - person ebrown; 12.06.2009

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

person StriplingWarrior    schedule 11.06.2009

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

person AllenG    schedule 11.06.2009
comment
Хорошая точка зрения. Я забыл упомянуть, что я подошел к этому вопросу после просмотра нескольких ответов, в которых для проверки типа использовался оператор if. - person jasonh; 11.06.2009

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

person thecoop    schedule 11.06.2009

Используется для получения объекта 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
    */
person Muhammad Awais    schedule 14.04.2016

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

person androidrill    schedule 03.04.2014
comment
typeof не определен в пространстве имен, это ключевое слово. System.IO не имеет к этому никакого отношения. - person Arturo Torres Sánchez; 18.09.2015

Тест производительности 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
person Alexander Vasilyev    schedule 03.08.2015
comment
Не следует использовать DateTime.UtcNow для измерения производительности. С вашим кодом, но с классом Stopwatch, я получил постоянно противоположные результаты для режима отладки. UseTypeOf: 00: 00: 14.5074469 UseGetType: 00: 00: 10.5799534. Режим выпуска такой же, как и ожидалось - person Alexey Shcherbak; 25.11.2015
comment
@AlexeyShcherbak Разница между секундомером и DateTime. Сейчас не может быть больше 10-20 мс, проверьте код еще раз. И меня не волнуют миллисекунды в моем тесте. Также мой код будет на несколько строк длиннее с секундомером. - person Alexander Vasilyev; 25.11.2015
comment
это плохая практика в целом, а не в вашем конкретном случае. - person Alexey Shcherbak; 25.11.2015
comment
@AlexanderVasilyev Количество строк кода никогда не должно использоваться в качестве аргумента, чтобы сделать что-то, задокументированно вводящее в заблуждение. Как показано в msdn.microsoft.com/ en-us / library / system.datetime (v = vs.110) .aspx, DateTime не следует использовать, если вас беспокоит время ниже 100 мс, поскольку он использует период времени. По сравнению с Stopwatch, который использует Tick процессоров, разрешение, используемое DateTime в Win7, составляет 15 мс. - person Eric Wu; 06.03.2016

person    schedule
comment
Пожалуйста, отредактируйте с дополнительной информацией. Не рекомендуется использовать только код и попробовать эти ответы, потому что они не содержат доступного для поиска контента и не объясняют, почему кто-то должен это попробовать. - person abarisone; 06.09.2016
comment
Ваш ответ не имеет отношения к вопросу. - person menxin; 05.09.2019