Sprawdzanie typu: typeof, GetType, czy jest?

Widziałem wiele osób używających następującego kodu:

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

Ale wiem, że ty też możesz to zrobić:

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

Lub to:

if (obj1 is int)
    // Some code here
Osobiście uważam, że ostatni jest najczystszy, ale czy czegoś mi brakuje? Który z nich jest najlepszy w użyciu, czy są to osobiste preferencje?
 1225
c#
Author: jasonh, 2009-06-11

14 answers

Wszystkie są inne.

  • typeof pobiera nazwę typu (którą określasz podczas kompilacji).
  • GetType pobiera Typ runtime instancji.
  • is zwraca true, jeśli instancja znajduje się w drzewie dziedziczenia.

Przykład

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

A co z typeof(T)? Czy jest on również rozwiązany w czasie kompilacji?

Tak. T jest zawsze tym, jaki jest typ wyrażenia. Pamiętaj, że metoda generyczna to w zasadzie cała masa metod z odpowiedni typ. Przykład:
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"
 1528
Author: Jimmy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-07 03:22:47

Użyj typeof, Gdy chcesz uzyskać typ w czasie kompilacji . Użyj GetType, Gdy chcesz uzyskać typ w Czas wykonania . Rzadko zdarzają się przypadki użycia is, ponieważ wykonuje ona rzut, a w większości przypadków i tak kończy się rzutem zmiennej.

Jest czwarta opcja, której nie rozważałeś (zwłaszcza jeśli zamierzasz oddać obiekt do typu, który również znajdziesz); Jest to użycie as.

Foo foo = obj as Foo;

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

Używa się tylko jednej obsady podejście to:

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

Wymaga dwóch.

 166
Author: Andrew Hare,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-22 10:16:03

1.

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

Jest to nielegalne, ponieważ typeof działa tylko na typach, a nie na zmiennych. Zakładam, że obj1 jest zmienną. W ten sposób typeof jest statyczny i wykonuje swoją pracę w czasie kompilacji zamiast w trybie runtime.

2.

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

To prawda, jeśli obj1 jest dokładnie typu int. Jeśli obj1 wywodzi się z int, warunek if będzie false.

3.

if (obj1 is int)

Jest to prawda, jeśli obj1 jest int, lub jeśli wywodzi się z klasy o nazwie int, lub jeśli implementuje interfejs o nazwie int.

 61
Author: Scott Langham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-11 19:17:26
Type t = typeof(obj1);
if (t == typeof(int))
    // Some code here
To jest błąd. Operator typeof w C# może przyjmować tylko nazwy typów, a nie obiektów.
if (obj1.GetType() == typeof(int))
    // Some code here
To zadziała, ale może nie tak, jak byś się spodziewał. Dla typów wartości, jak pokazałeś tutaj, jest to dopuszczalne, ale dla typów referencyjnych zwracałoby to true tylko wtedy, gdy typ jest typem dokładnie takim samym, a nie czymś innym w hierarchii dziedziczenia. Na przykład:
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");
}

To wydrukuje "o is something else", ponieważ typem o jest Dog, a nie Animal. Możesz zrób to jednak, jeśli użyjesz metody IsAssignableFrom klasy Type.

if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type
    Console.WriteLine("o is an animal");
[38]}ta technika wciąż pozostawia jednak poważny problem. Jeśli zmienna ma wartość null, wywołanie GetType() spowoduje wywołanie NullReferenceException. Tak więc, aby to działało poprawnie, wykonałbyś:
if(o != null && typeof(Animal).IsAssignableFrom(o.GetType()))
    Console.WriteLine("o is an animal");

Z tym, masz równoważne zachowanie słowa kluczowego is. Dlatego, jeśli takie zachowanie chcesz, powinieneś użyć słowa kluczowego is, które jest bardziej czytelne i wydajniejsze.

if(o is Animal)
    Console.WriteLine("o is an animal");

W w większości przypadków słowo kluczowe is nadal nie jest tym, czego naprawdę chcesz, ponieważ zwykle nie wystarczy wiedzieć, że obiekt jest określonego typu. Zazwyczaj, chcesz rzeczywiście używać tego obiektu jako instancji tego typu, co wymaga również odlewania go. I tak możesz znaleźć się pisząc kod w ten sposób:

if(o is Animal)
    ((Animal)o).Speak();

Ale to sprawia, że CLR sprawdza typ obiektu nawet dwa razy. Sprawdzi go raz, aby spełnić operator is, a jeśli o jest rzeczywiście Animal, sprawdzamy ponownie, aby potwierdzić Obsada.

Zamiast tego jest to bardziej efektywne:]}
Animal a = o as Animal;
if(a != null)
    a.Speak();

Operator as jest obsadą, która nie wyrzuci wyjątku, jeśli się nie powiedzie, zamiast tego zwróci null. W ten sposób CLR sprawdza typ obiektu tylko raz, a po tym, po prostu musimy zrobić sprawdzenie null, co jest bardziej efektywne.

Ale uważaj: wiele osób wpada w pułapkę z as. Ponieważ nie rzuca WYJĄTKÓW, niektórzy uważają go za " Bezpieczny" rzucają i używają go wyłącznie, unikając regularnych odlewów. Prowadzi to do błędów takich jak:

(o as Animal).Speak();

W tym przypadku deweloper zakłada, że o będzie zawsze będzie Animal i tak długo, jak ich założenie jest poprawne, wszystko działa dobrze. Ale jeśli się mylą, to kończą z NullReferenceException. Przy regularnej obsadzie otrzymaliby zamiast tego InvalidCastException, który lepiej zidentyfikowałby problem.

Czasami ten błąd może być trudno znaleźć:

class Foo{
    readonly Animal animal;

    public Foo(object o){
        animal = o as Animal;
    }

    public void Interact(){
        animal.Speak();
    }
}

Jest to kolejny przypadek, w którym deweloper wyraźnie spodziewa się, że o będzie Animal za każdym razem, ale nie jest to oczywiste w konstruktorze, w którym używany jest as cast. Nie jest to oczywiste, dopóki nie dojdziesz do metody Interact, gdzie oczekuje się, że pole animal będzie dodatnio przypisane. W tym przypadku nie tylko kończysz z wprowadzającym w błąd wyjątkiem, ale zostanie on wyrzucony dopiero potencjalnie znacznie później niż w momencie wystąpienia rzeczywistego błędu.

W podsumowanie:

  • Jeśli chcesz tylko wiedzieć, czy obiekt jest jakiegoś typu, użyj is.

  • Jeśli chcesz traktować obiekt jako instancję określonego typu, ale nie masz pewności, czy obiekt będzie tego typu, użyj as i sprawdź, czy nie ma null.

  • Jeśli chcesz traktować obiekt jako instancję określonego typu, A obiekt ma być tego typu, użyj zwykłego odlewu.

 40
Author: P Daddy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-11 17:02:47

Miałem Type-właściwość do porównania i nie mogłem użyć is (Jak my_type is _BaseTypetoLookFor), ale mogłem użyć tych:

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

Zauważ, że IsInstanceOfType i IsAssignableFrom zwracają true podczas porównywania tych samych typów, gdzie IsSubClassOf zwróci false. I IsSubclassOf nie działa na interfejsach, gdzie dwa pozostałe działają. (Zobacz także to pytanie i odpowiedź .)

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
 11
Author: Yahoo Serious,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:37

Jeśli używasz C # 7, to czas na aktualizację świetnej odpowiedzi Andrew Hare ' a. Pattern matching wprowadził ładny skrót, który daje nam wpisaną zmienną w kontekście instrukcji if, bez konieczności oddzielnej deklaracji / cast i check:

if (obj1 is int integerValue)
{
    integerValue++;
}

Wygląda to dość rozczarowująco jak na taką pojedynczą obsadę, ale naprawdę świeci, gdy masz wiele możliwych typów w swojej rutynie. Poniżej znajduje się stary sposób na uniknięcie rzucania dwukrotnie:

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

Praca nad zmniejszeniem tego kodu w jak największym stopniu, jak również unikanie duplikatów odlewów tego samego obiektu zawsze mi przeszkadzało. Powyższe jest ładnie skompresowane z wzorcem pasującym do następującego:

switch (obj1)
{
    case Button button:
        // do stuff...
        break;
    case TextBox text:
        // do stuff...
        break;
    case Label label:
        // do stuff...
        break;
    // and so on...
}

EDIT: Zaktualizowano dłuższą nową metodę, aby użyć przełącznika zgodnie z komentarzem Palec.

 8
Author: JoelC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-26 19:56:38

Wolę to

To powiedziawszy, jeśli używasz to , prawdopodobnie nie używasz dziedziczenia prawidłowo.

Załóżmy, że osoba: byt, i że zwierzę: byt. Feed jest wirtualną metodą w Entity (aby uczynić Neila szczęśliwym)

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

Raczej

class Person
{
  public override void Feed( Person p )
  {
    // feed the person
  }
  public override void Feed( Animal a )
  {
    // feed the animal
  }
}
 7
Author: bobobobo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-23 14:10:23

Wydaje mi się, że ten ostatni dotyczy również dziedziczenia (np. pies to zwierzę == prawda), co w większości przypadków jest lepsze.

 5
Author: StriplingWarrior,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-11 19:14:00

To zależy od tego, co robię. Jeśli potrzebuję wartości bool (powiedzmy, aby określić, czy będę rzucać do int), użyję is. Jeśli rzeczywiście potrzebuję tego typu z jakiegoś powodu (powiedzmy, aby przejść do innej metody) użyję GetType().

 2
Author: AllenG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-11 19:20:16

Ostatni jest czystszy, bardziej oczywisty, a także sprawdza podtypy. Pozostałe nie sprawdzają polimorfizmu.

 0
Author: thecoop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-11 19:16:12

Używane do uzyskania systemu.Obiekt typu typ. Wyrażenie typeof przyjmuje następującą postać:

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
    */

Ta próbka używa metody GetType do określenia typu, który ma zawierać wynik obliczeń numerycznych. Zależy to od wymagań przechowywania wynikowego numeru.

    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
Author: Muhammad Awais,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-14 07:52:21
if (c is UserControl) c.Enabled = enable;
 -3
Author: Paulos02,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-06 13:18:46

Możesz użyć operatora "typeof ()" w C#, ale musisz wywołać przestrzeń nazw używając System.IO; musisz użyć słowa kluczowego "is", jeśli chcesz sprawdzić typ.

 -5
Author: androidrill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-07 07:01:41

Test wydajności 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();
    }
}

Wyniki w trybie debugowania:

00:00:08.4096636
00:00:10.8570657

Wyniki w trybie release:

00:00:02.3799048
00:00:07.1797128
 -5
Author: Alexander Vasilyev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-03 13:58:53