it-swarm.com.de

Typüberprüfung: typeof, GetType oder ist?

Ich habe gesehen, dass viele Leute den folgenden Code verwenden:

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

Aber ich weiß, dass Sie das auch tun können:

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

Oder dieses:

if (obj1 is int)
    // Some code here

Ich persönlich halte den letzten für den saubersten, aber fehlt mir etwas? Welches ist am besten zu gebrauchen, oder ist es eine persönliche Präferenz?

1406
jasonh

Alle sind unterschiedlich.

  • typeof hat einen Typnamen (den Sie beim Kompilieren angeben).
  • GetType ruft den Laufzeittyp einer Instanz ab.
  • is gibt true zurück, wenn sich eine Instanz im Vererbungsbaum befindet.

Beispiel

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

Was ist mit typeof(T)? Wird es auch zur Kompilierungszeit aufgelöst?

Ja. T ist immer der Typ des Ausdrucks. Denken Sie daran, dass eine generische Methode im Grunde genommen eine ganze Reihe von Methoden mit dem entsprechenden Typ ist. Beispiel:

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"
1723
Jimmy

Verwenden Sie typeof, wenn Sie den Typ zur Kompilierungszeit abrufen möchten . Verwenden Sie GetType, wenn Sie den Typ zur Ausführungszeit abrufen möchten . Es gibt selten Fälle, in denen is verwendet werden muss, da es sich um eine Umwandlung handelt. In den meisten Fällen wird die Variable ohnehin umgewandelt.

Es gibt eine vierte Option, die Sie nicht in Betracht gezogen haben (insbesondere, wenn Sie ein Objekt auf den Typ umwandeln möchten, den Sie ebenfalls finden). das heißt, as zu verwenden.

Foo foo = obj as Foo;

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

Dies verwendet nur one cast, während dieser Ansatz:

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

erfordert zwei.

180
Andrew Hare

1.

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

Dies ist illegal, da typeof nur für Typen funktioniert, nicht für Variablen. Ich nehme an, obj1 ist eine Variable. Auf diese Weise ist typeof statisch und erledigt seine Arbeit zur Kompilierungszeit anstatt zur Laufzeit.

2.

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

Dies ist wahr, wenn obj1 genau vom Typ int ist. Wenn obj1 von int abgeleitet ist, ist die if-Bedingung falsch.

.

if (obj1 is int)

Dies ist der Fall, wenn obj1 ein int ist oder von einer Klasse mit dem Namen int abgeleitet ist oder eine Schnittstelle mit dem Namen int implementiert.

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

Das ist ein Fehler. Der Operator typeof in C # kann nur Typnamen annehmen, keine Objekte.

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

Dies wird funktionieren, aber vielleicht nicht so, wie Sie es erwarten würden. Wie Sie hier gezeigt haben, ist es für Werttypen akzeptabel, aber für Referenztypen würde es nur true zurückgeben, wenn der Typ der exakt derselbe -Typ wäre, und nicht etwas anderes in der Vererbungshierarchie. Zum Beispiel:

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

Dies würde "o is something else" ausgeben, da der Typ von oDog ist, nicht Animal. Sie können diese Funktion jedoch ausführen, wenn Sie die Methode IsAssignableFrom der Klasse Type verwenden.

if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type
    Console.WriteLine("o is an animal");

Diese Technik ist jedoch immer noch ein großes Problem. Wenn Ihre Variable null ist, löst der Aufruf von GetType() eine NullReferenceException aus. Damit es richtig funktioniert, machen Sie Folgendes:

if(o != null && typeof(Animal).IsAssignableFrom(o.GetType()))
    Console.WriteLine("o is an animal");

Damit haben Sie das gleiche Verhalten wie mit dem Schlüsselwort is. Wenn dies das gewünschte Verhalten ist, sollten Sie das Schlüsselwort is verwenden, das besser lesbar und effizienter ist.

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

In den meisten Fällen ist das Schlüsselwort is jedoch immer noch nicht das, was Sie wirklich wollen, da es normalerweise nicht ausreicht, nur zu wissen, dass ein Objekt von einem bestimmten Typ ist. Normalerweise möchten Sie das Objekt tatsächlich verwenden als eine Instanz dieses Typs, für die auch ein Casting erforderlich ist. Und so schreiben Sie vielleicht folgenden Code:

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

Dadurch überprüft die CLR den Objekttyp jedoch bis zu zweimal. Es wird einmal überprüft, um den Operator is zu erfüllen, und wenn o tatsächlich ein Animal ist, wird es erneut überprüft, um die Umwandlung zu validieren.

Es ist effizienter, dies stattdessen zu tun:

Animal a = o as Animal;
if(a != null)
    a.Speak();

Der Operator as ist eine Besetzung, die bei einem Fehlschlagen keine Ausnahme auslöst und stattdessen null zurückgibt. Auf diese Weise überprüft die CLR den Objekttyp nur einmal, und danach müssen wir nur noch eine Nullprüfung durchführen, was effizienter ist.

Aber Vorsicht: Viele Menschen geraten mit as in eine Falle. Da es keine Ausnahmen auslöst, betrachten manche Leute es als "sichere" Besetzung und verwenden es ausschließlich, indem sie normale Besetzungen meiden. Dies führt zu Fehlern wie diesen:

(o as Animal).Speak();

In diesem Fall geht der Entwickler eindeutig davon aus, dass oimmer ein Animal ist. Solange diese Annahme korrekt ist, funktioniert alles einwandfrei. Aber wenn sie sich irren, dann ist das, was sie am Ende hier haben, ein NullReferenceException. Mit einer regulären Besetzung hätten sie stattdessen ein InvalidCastException erhalten, das das Problem genauer identifiziert hätte.

Manchmal ist dieser Fehler schwer zu finden:

class Foo{
    readonly Animal animal;

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

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

Dies ist ein weiterer Fall, in dem der Entwickler eindeutig erwartet, dass o jedes Mal ein Animal ist, dies ist jedoch in dem Konstruktor nicht offensichtlich, in dem der Cast as verwendet wird. Es ist nicht offensichtlich, bis Sie zur Methode Interact gelangen, bei der erwartet wird, dass das Feld animal positiv zugewiesen wird. In diesem Fall kommt es nicht nur zu einer irreführenden Ausnahme, sondern die Auslösung erfolgt möglicherweise erst viel später, als der eigentliche Fehler aufgetreten ist.

In Summe:

  • Wenn Sie nur wissen möchten, ob es sich um ein Objekt handelt, verwenden Sie is.

  • Wenn Sie ein Objekt als eine Instanz eines bestimmten Typs behandeln müssen, aber nicht sicher sind, ob das Objekt von diesem Typ ist, verwenden Sie as und suchen Sie nach null.

  • Wenn Sie ein Objekt als eine Instanz eines bestimmten Typs behandeln müssen und das Objekt von diesem Typ sein soll, verwenden Sie eine reguläre Besetzung.

48
P Daddy

Ich hatte eine Type- -Eigenschaft zum Vergleichen und konnte is nicht verwenden (wie my_type is _BaseTypetoLookFor), aber ich konnte diese verwenden:

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

Beachten Sie, dass IsInstanceOfType und IsAssignableFrom beim Vergleich derselben Typen true zurückgeben, wobei IsSubClassOf false zurückgibt. Und IsSubclassOf funktioniert nicht auf Schnittstellen, wo die anderen beiden tun. (Siehe auch diese Frage und Antwort .)

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
13
Yahoo Serious

Wenn Sie C # 7 verwenden, ist es Zeit für ein Update der großartigen Antwort von Andrew Hare. Mustervergleich hat eine nette Verknüpfung eingeführt, die uns eine typisierte Variable im Kontext der if-Anweisung gibt, ohne dass eine separate Deklaration/Umwandlung und Prüfung erforderlich ist:

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

Das sieht für eine einzelne Besetzung wie diese ziemlich überwältigend aus, strahlt aber wirklich, wenn Sie viele mögliche Typen in Ihre Routine einbeziehen. Das Folgende ist der alte Weg, um das zweimalige Wirken zu vermeiden:

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

Es hat mich immer gestört, diesen Code so weit wie möglich zu verkleinern und doppelte Besetzungen desselben Objekts zu vermeiden. Das oben Genannte ist gut komprimiert und mit folgenden Mustern versehen:

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

BEARBEITEN: Aktualisierung der längeren neuen Methode zur Verwendung eines Schalters gemäß Palecs Kommentar.

12
JoelC

Ich bevorzuge ist

Das heißt, wenn Sie verwenden ist, Sie werden wahrscheinlich nicht Vererbung richtig verwenden.

Angenommen, diese Person: Entität und dieses Tier: Entität. Feed ist eine virtuelle Methode in Entity (um Neil glücklich zu machen)

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

Lieber

class Person
{
  public override void Feed( Person p )
  {
    // feed the person
  }
  public override void Feed( Animal a )
  {
    // feed the animal
  }
}
9
bobobobo

Ich glaube, der Letzte befasst sich auch mit Vererbung (z. B. Hund ist Tier == wahr), was in den meisten Fällen besser ist.

5

Es kommt darauf an, was ich tue. Wenn ich einen bool-Wert benötige (zum Beispiel, um festzustellen, ob ich in ein int umgewandelt werde), verwende ich is. Wenn ich den Typ tatsächlich aus irgendeinem Grund brauche (um ihn beispielsweise an eine andere Methode zu übergeben), verwende ich GetType().

2
AllenG

Wird verwendet, um das System.Type-Objekt für einen Typ abzurufen. Ein typeof Ausdruck hat die folgende Form:

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

In diesem Beispiel wird mit der GetType-Methode der Typ bestimmt, der das Ergebnis einer numerischen Berechnung enthält. Dies hängt von den Speicheranforderungen der resultierenden Nummer ab.

    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
Muhammad Awais

Der letzte ist sauberer, offensichtlicher und prüft auch auf Untertypen. Die anderen prüfen nicht auf Polymorphismus.

0
thecoop