Проверка типа: 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 19:10:28
Не забывайте as!
RCIX 7.10.2009 23:52:47
asхотя на самом деле это не проверка типов ...
jasonh 8.10.2009 00:48:03
asэто, безусловно, форма проверки типов, каждая isтакая же, как есть! Он эффективно используется isза кулисами и повсеместно используется в MSDN там, где он улучшает чистоту кода по сравнению с ним is. Вместо того, чтобы проверять isсначала, вызов asустанавливает типизированную переменную, которая готова к использованию: если она пуста, ответьте соответственно; в противном случае продолжайте. Конечно то, что я видел и использовал совсем немного.
Zaccone 20.08.2014 16:07:00
Существует значительная разница в производительности в пользу as/ is(описана в stackoverflow.com/a/27813381/477420 ), если предположить, что его семантические работы подходят для вашего случая.
Alexei Levenkov 7.01.2015 06:46:05
@ samusarin это не «использует» отражение. GetTypeМетод вы ссылаетесь в System.Reflection.Assembly- совершенно другим способом и не имеет значения здесь.
Kirk Woll 11.08.2017 20:59:17
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"
1826
7.08.2018 03:22:47
Ах, так что, если у меня есть класс Ford, производный от Car, и экземпляр Ford, проверка «is Car» в этом случае будет верной. Имеет смысл!
jasonh 11.06.2009 19:19:33
Чтобы уточнить, я знал об этом, но я прокомментировал, прежде чем вы добавили пример кода. Я хотел бы попытаться добавить ясность английского в ваш и без того отличный ответ.
jasonh 11.06.2009 19:25:13
@Shimmy, если typeof вычисляется во время компиляции, а GetType () оценивается во время выполнения, то имеет смысл, что GetType () подвергается небольшому снижению производительности
Cedric Mamo 10.12.2012 13:35:06
как насчет нового Dog (). GetType () это Animal Или typeof (Dog) это Animal, он просто выдает предупреждение, а не ошибку?
Prerak K 7.05.2014 06:29:23
@PrerakK new Dog().GetType() is Animalвозвращает false (и вашу другую версию), поскольку .GetType()возвращает объект типа Type, а Typeне является Animal.
Maarten 7.11.2014 09:45:46

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

5
11.06.2009 19:14:00

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

2
11.06.2009 19:20:16
Хорошая точка зрения. Я забыл упомянуть, что добрался до этого вопроса после просмотра нескольких ответов, в которых для проверки типа использовался оператор if.
jasonh 11.06.2009 19:20:47

Используйте, 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+ , теперь вы можете использовать inline, поэтому подход «is» теперь может быть реализован и в одном.

Пример:

if(obj is Foo newLocalFoo)
{
    // For example, you can now reference 'newLocalFoo' in this local scope
    Console.WriteLine(newLocalFoo);
}
193
29.01.2020 14:57:30
С изменениями в .NET 4 все isеще выполняет приведение?
ahsteele 21.03.2013 21:04:51
Этот ответ правильный? Правда ли, что вы действительно можете передать экземпляр в typeof ()? Мой опыт был Нет. Но я полагаю, что в общем случае проверка экземпляра может происходить во время выполнения, тогда как проверка класса должна выполняться во время компиляции.
Jon Coombs 8.10.2013 02:13:53
@jon (через 4 года после вашего q.), нет, вы не можете передать экземпляр typeof(), и этот ответ не предполагает, что вы можете. Вместо этого вы передаете тип, т. Е. typeof(string)Работает, typeof("foo")а не.
Abel 20.09.2017 01:30:59
Я не верю, что isвыполняет приведение как таковую, скорее специальную операцию в IL.
abatishchev 22.10.2018 20:20:08
Теперь мы можем сделатьif (obj is Foo foo) { /* use foo here */ }
Ivan García Topete 17.04.2019 20:49:38

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

Тем не менее, если вы используете 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
  }
}
9
23.10.2013 14:10:23
Правда, я бы никогда не сделал первого, зная, что Персона происходит от Животного.
jasonh 11.06.2009 19:22:01
Последний тоже не использует наследование. Foo должен быть виртуальным методом Entity, который переопределяется в Person и Animal.
Neil Williams 11.06.2009 19:31:30
@ Bobobobo Я думаю, что вы имеете в виду «перегрузка», а не «наследование».
lc. 11.06.2009 19:42:10
@lc: Нет, я имею в виду наследование. Первый пример - это своего рода неправильный способ (использование is ) для получения другого поведения. Второй пример использует перегрузку yes, но избегает использования is .
bobobobo 11.06.2009 19:47:45
Проблема с примером в том, что он не будет масштабироваться. Если вы добавили новые сущности, которые нужно было съесть (например, насекомое или монстр), вам нужно было бы добавить новый метод в классе Entity, а затем переопределить его в подклассах, которые будут его кормить. Это не более предпочтительно, чем список, если (сущность - это X), иначе (сущность - это Y) ... Это нарушает LSP и OCP, наследование, вероятно, не лучшее решение проблемы. Некоторая форма делегирования, вероятно, будет предпочтительнее.
ebrown 11.06.2009 21:12:22

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

0
11.06.2009 19:16:12

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.

70
11.06.2009 19:17:26
Думая о 1, вы правы. И все же я видел это в нескольких примерах кода здесь. Это должно быть Type t = obj1.GetType ();
jasonh 11.06.2009 19:26:47
Да, я так думаю. "typeof (obj1)" не компилируется, когда я пытаюсь это сделать.
Scott Langham 11.06.2009 19:39:42
Невозможно получить из System.Int32 или любого другого типа значения в C #
reggaeguitar 4.12.2014 19:34:36
Можете ли вы сказать, что было бы typeof (typeof (system.int32))
Sana 27.06.2018 12:33:24
@ Сана, почему бы тебе не попробовать :) Я думаю, ты получишь экземпляр System.Type, который представляет тип System.Type! Документация для typeof находится здесь: docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
Scott Langham 2.07.2018 10:46:42
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.

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

53
11.01.2017 17:02:47
что не так с этим, если (o является Animal) ((Animal) o) .Speak (); ? Можете ли вы дать более подробную информацию?
batmaci 10.01.2017 22:11:31
@batmaci: он в ответе - он вызывает две проверки типов. В первый раз o is Animal, что требует CLR , чтобы проверить , если тип переменной oявляется Animal. Второй раз, когда он проверяет, когда он бросает в заявлении ((Animal)o).Speak(). Вместо того, чтобы проверять дважды, проверьте один раз, используя as.
siride 28.07.2018 15:15:57
Я нашел это абсолютно отличное объяснение, спасибо за разъяснения!
Paul Efford 29.11.2019 08:31:02

Я имел Type-property для сравнения и не мог использовать 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
14
23.05.2017 10:31:37

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

-5
7.04.2014 07:01:41
typeofне определен в пространстве имен, это ключевое слово. System.IOне имеет к этому никакого отношения.
Arturo Torres Sánchez 18.09.2015 00:13:36

Тест производительности 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();
    }
}

Результаты в режиме отладки:

00:00:08.4096636
00:00:10.8570657

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

00:00:02.3799048
00:00:07.1797128
-5
3.08.2015 13:58:53
Не следует использовать DateTime.UtcNow для оценки производительности. С вашим кодом, но с классом Stopwatch я получил постоянно противоположные результаты для режима отладки. UseTypeOf: 00: 00: 14.5074469 UseGetType: 00: 00: 10.5799534. Режим релиза такой же, как и ожидалось
Alexey Shcherbak 25.11.2015 03:46:27
@AlexeyShcherbak Разница между секундомером и DateTime.Now не может быть более 10-20 мс, проверьте код еще раз. И мне плевать на миллисекунды в моем тесте. Также мой код будет на несколько строк длиннее с секундомером.
Alexander Vasilyev 25.11.2015 09:21:12
Это плохая практика в целом, а не в вашем конкретном случае.
Alexey Shcherbak 25.11.2015 09:23:32
@AlexanderVasilyev Количество строк кода никогда не должно использоваться в качестве аргумента, чтобы сделать что-то документально вводящее в заблуждение. Как видно из msdn.microsoft.com/en-us/library/system.datetime(v=vs.110).aspx , DateTimeего не следует использовать, если вы беспокоитесь о временах ниже 100 мс , поскольку он использует временные рамки ОС. По сравнению с тем Stopwatch, в котором используются процессоры Tick, разрешение, используемое DateTimeв Win7, составляет колоссальные 15 мс.
Eric Wu 6.03.2016 05:54:36

Используется для получения объекта 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
    */
0
14.04.2016 07:52:21
if (c is UserControl) c.Enabled = enable;
-4
6.09.2016 13:18:46
Пожалуйста, отредактируйте с дополнительной информацией. Ответы «только код» и «попробуй это» не приветствуются, потому что они не содержат контента для поиска и не объясняют, почему кто-то должен «попробовать это».
abarisone 6.09.2016 14:33:12
Ваш ответ не имеет отношения к вопросу.
menxin 5.09.2019 10:48:32

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

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

16
26.06.2018 19:56:38
В этом случае рекомендуется использовать switchоператор с сопоставлением с образцом .
Palec 10.06.2018 17:41:29
Как бы вы справились с собой? В этом конкретном блоке кода? if (obj1 is int integerValue) { integerValue++; }
Ben Vertonghen 25.02.2019 22:28:32
Бен, если я понимаю твой вопрос, у меня просто есть еще оператор для обработки других случаев, так как ты не можешь поместить нецелое число в целочисленную переменную. :)
JoelC 28.02.2019 13:53:29