La Vérification du Type: typeof, GetType, ou est?

j'ai vu beaucoup de gens utiliser le code suivant:

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

mais je sais que vous pourriez aussi faire ceci:

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

ou ceci:

if (obj1 is int)
    // Some code here

personnellement, je pense que le dernier est le plus propre, mais y a-t-il quelque chose que je manque? Laquelle est la meilleure à utiliser, ou est-ce une préférence personnelle?

1225
c#
demandé sur jasonh 2009-06-11 23:10:28
la source

14 ответов

Tous sont différents.

  • typeof prend un nom de type (que vous spécifiez au moment de la compilation).
  • GetType obtient le type d'exécution d'une instance.
  • is retourne true si une instance est dans l'arbre de l'héritage.

exemple

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

et typeof(T) ? Est-il également résolu au moment de la compilation?

Oui. T est toujours le type d'expression. Rappelez-vous, une méthode générique est fondamentalement un tas de méthodes avec le type approprié. Exemple:

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
répondu Jimmy 2018-08-07 06:22:47
la source

utilisez typeof lorsque vous voulez obtenir le type à temps de compilation . Utilisez GetType lorsque vous voulez obtenir le type à exécution time . Il ya rarement des cas à utiliser is comme il le fait un moulage et, dans la plupart des cas, vous finissez par couler la variable de toute façon.

il y a une quatrième option que vous n'avez pas considérée (surtout si vous allez lancer un objet vers le type que vous trouvez aussi bien); qui est pour utiliser as .

Foo foo = obj as Foo;

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

cela utilise seulement un cast alors que cette approche:

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

exige deux .

166
répondu Andrew Hare 2013-02-22 14:16:03
la source

1.

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

c'est illégal, car typeof ne fonctionne que sur les types, pas sur les variables. Je suppose que obj1 est une variable. De cette façon typeof est statique, et fait son travail au moment de la compilation au lieu de l'exécution.

2.

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

ceci est vrai si obj1 est exactement de type int. Si obj1 dérive de int, la condition if sera false.

3.

if (obj1 is int)

Cela est vrai si obj1 est un int, ou si elle découle d'une classe appelée int, ou si elle implémente une interface, appelée int.

61
répondu Scott Langham 2009-06-11 23:17:26
la source
Type t = typeof(obj1);
if (t == typeof(int))
    // Some code here

C'est une erreur. L'opérateur typeof en C# ne peut prendre que les noms de type, pas les objets.

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

ça va marcher, mais peut-être pas comme vous vous y attendiez. Pour les types de valeurs, comme vous l'avez montré ici, c'est acceptable, mais pour les types de référence, cela ne retournerait vrai que si le type était le type exact same , pas quelque chose d'autre dans la hiérarchie de l'héritage. Par exemple:

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

This serait d'imprimer "o is something else" , parce que le type de o est Dog , pas Animal . Vous pouvez faire ce travail, cependant, si vous utilisez la méthode IsAssignableFrom de la classe Type .

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

cette technique laisse encore un problème majeur, cependant. Si votre variable est nulle, l'appel à GetType() lancera une NullReferenceException. Donc pour que ça marche correctement, vous feriez:

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

avec ceci, vous avez comportement équivalent du mot clé is . Par conséquent, si c'est le comportement que vous voulez, vous devez utiliser le is mot-clé, qui est plus lisible et plus efficace.

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

dans la plupart des cas, cependant, le mot-clé is n'est toujours pas ce que vous voulez vraiment, parce qu'il n'est généralement pas suffisant juste de savoir qu'un objet est d'un certain type. Habituellement, vous voulez réellement utiliser cet objet comme une instance de ce type, qui nécessite casting aussi. Et donc vous pouvez vous retrouver à écrire du code comme ceci:

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

mais cela fait le CLR vérifier le type de l'objet jusqu'à deux fois. Il le vérifiera une fois pour satisfaire l'opérateur is , et si o est bien un Animal , nous le ferons vérifier à nouveau pour valider la distribution.

il est plus efficace de le faire à la place:

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

l'opérateur as est un cast cela ne va pas jeter une exception si elle échoue, au lieu de retourner null . De cette façon, le CLR vérifie le type de l'objet juste une fois, et après cela, nous avons juste besoin de faire une vérification nulle, ce qui est plus efficace.

Mais attention: beaucoup de gens tombent dans un piège avec as . Parce qu'il ne jette pas d'exceptions, certaines personnes pensent à lui comme un casting "sûr", et ils l'utilisent exclusivement, fuyant des casts réguliers. Cela conduit à des erreurs comme celle-ci:

(o as Animal).Speak();

dans ce cas, le développeur suppose clairement que o sera toujours être un Animal , et tant que leur hypothèse est correcte, tout fonctionne bien. Mais s'ils ont tort, alors ce qu'ils finissent ici est un NullReferenceException . Avec un moulage régulier, ils auraient eu un InvalidCastException à la place, ce qui aurait plus correctement identifié le problème.

Parfois, ce bug peut être difficile à trouver:

class Foo{
    readonly Animal animal;

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

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

il s'agit d'un autre cas où le développeur s'attend clairement à ce que o soit un Animal à chaque fois, mais ce n'est pas évident dans le constructeur, où la fonte as est utilisée. Ce n'est pas évident jusqu'à ce que vous arriviez à la méthode Interact , où le champ animal devrait être attribué positivement. Dans ce cas, non seulement vous finissez avec une exception trompeuse, mais il n'est pas jeté jusqu'à ce que potentiellement beaucoup plus tard que lorsque le réel erreur s'est produite.

en résumé:

  • Si vous avez seulement besoin de savoir si un objet est d'un certain type, utiliser is .

  • si vous devez traiter un objet comme une instance d'un certain type, mais vous ne savez pas avec certitude que l'objet sera de ce type, utilisez as et vérifiez null .

  • If vous avez besoin pour traiter un objet comme une instance d'un certain type, et l'objet est censé être de ce type, l'utilisation régulière d'un casting.

40
répondu P Daddy 2017-01-11 20:02:47
la source

j'ai eu un Type - propriété à comparer à et ne pouvait pas utiliser is (comme my_type is _BaseTypetoLookFor ), mais je pouvais utiliser ces:

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

Avis IsInstanceOfType et IsAssignableFrom retour true lorsque l'on compare les mêmes types, où IsSubClassOf sera de retour false . Et IsSubclassOf ne fonctionne pas sur les interfaces, où les deux autres. (Voir aussi cette question et réponse .)

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
répondu Yahoo Serious 2017-05-23 13:31:37
la source

si vous utilisez C# 7, alors il est temps pour une mise à jour de la grande réponse D'Andrew Hare. Pattern matching a introduit un raccourci agréable qui nous donne une variable dactylographiée dans le contexte de la déclaration if, sans exiger une déclaration séparée/moulage et de contrôle:

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

cela semble assez décevant pour un casting simple comme celui-ci, mais brille vraiment quand vous avez beaucoup de types possibles venir dans votre routine. Ci-dessous est la ancienne manière d'éviter de jeter à deux reprises:

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

travailler autour de réduire ce code autant que possible, ainsi que d'éviter les doubles moulages du même objet m'a toujours dérangé. Ce qui précède est bien comprimé avec un motif correspondant à ce qui suit:

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

EDIT: mise à Jour la plus nouvelle méthode pour utiliser un interrupteur comme par Palec commentaire.

8
répondu JoelC 2018-06-26 22:56:38
la source

je préfère est

"

cela dit, si vous utilisez est , vous êtes probablement pas utiliser l'héritage correctement.

suppose que personne : entité, et cet Animal : entité. L'alimentation est une méthode virtuelle au sein de l'Entité (pour faire de Neil heureux)

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

plutôt

class Person
{
  public override void Feed( Person p )
  {
    // feed the person
  }
  public override void Feed( Animal a )
  {
    // feed the animal
  }
}
7
répondu bobobobo 2013-10-23 18:10:23
la source

je crois que le dernier se penche aussi sur l'héritage (par exemple chien est Animal == vrai), ce qui est mieux dans la plupart des cas.

5
répondu StriplingWarrior 2009-06-11 23:14:00
la source

ça dépend de ce que je fais. Si j'ai besoin d'une valeur bool (par exemple, pour déterminer si je vais lancer à un int), je vais utiliser is . Si j'ai réellement besoin du type pour une raison quelconque (disons, passer à une autre méthode) j'utiliserai GetType() .

2
répondu AllenG 2009-06-11 23:20:16
la source

le dernier est plus propre, plus évident, et vérifie également les sous-types. Les autres ne vérifient pas le polymorphisme.

0
répondu thecoop 2009-06-11 23:16:12
la source

utilisé pour obtenir le système.Type objet pour un type. Un type d'expression prend la forme suivante:

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

Cet exemple utilise la méthode GetType pour déterminer le type qui est utilisé pour contenir le résultat d'un calcul numérique. Cela dépend des besoins de stockage du nombre résultant.

    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
répondu Muhammad Awais 2016-04-14 10:52:21
la source
if (c is UserControl) c.Enabled = enable;
-3
répondu Paulos02 2016-09-06 16:18:46
la source

vous pouvez utiliser l'opérateur "typeof ()" dans C# mais vous devez appeler L'espace de noms en utilisant le système.IO; vous devez utiliser le mot-clé " is " Si vous souhaitez vérifier un type.

-5
répondu androidrill 2014-04-07 11:01:41
la source

Performance test 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();
    }
}

Résultats en mode débogage:

00:00:08.4096636
00:00:10.8570657

Résultats dans le mode de libération:

00:00:02.3799048
00:00:07.1797128
-5
répondu Alexander Vasilyev 2015-08-03 16:58:53
la source

Autres questions sur c#