variable - typeof c#




Type de vérification: typeof, GetType ou est? (10)

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 tu pourrais aussi faire ceci:

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

Ou ca:

if (obj1 is int)
    // Some code here

Personnellement, je sens que le dernier est le plus propre, mais y a-t-il quelque chose qui me manque? Lequel est le meilleur à utiliser, ou est-ce une préférence personnelle?


Utilisé pour obtenir l'objet System.Type pour un type. Une expression typeof 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 utilisé pour contenir le résultat d'un calcul numérique. Cela dépend des exigences 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
    */

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


Je crois que le dernier regarde aussi l'héritage (par exemple Dog is Animal == true), ce qui est mieux dans la plupart des cas.


Je préfère est

Cela dit, si vous utilisez, vous n'utilisez probablement pas l' héritage correctement.

Supposons que Person: Entity et Animal: Entity. Feed est une méthode virtuelle dans Entity (pour rendre 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
  }
}

Si vous utilisez C # 7, alors il est temps de mettre à jour la bonne réponse d'Andrew Hare. Pattern matching a introduit un joli raccourci qui nous donne une variable typée dans le contexte de l'instruction if, sans nécessiter une déclaration / cast séparée et vérifier:

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

Cela semble assez décevant pour un seul casting comme celui-ci, mais brille vraiment quand vous avez beaucoup de types possibles entrant dans votre routine. Le ci-dessous est l'ancienne façon d'éviter de lancer deux fois:

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étrécir ce code autant que possible, ainsi que d'éviter les doublons du même objet m'a toujours dérangé. Ce qui précède est bien compressé avec un motif correspondant à ce qui suit:

if (obj1 is Button button)
{
    // do stuff...
}
else if (obj1 is TextBox text)
{
    // do stuff...
}
else if (obj1 is Label label)
{
    // do stuff...
}
// ... and so on

Test de performance 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 en mode de publication:

00:00:02.3799048
00:00:07.1797128

Utilisez typeof lorsque vous voulez obtenir le type au moment de la compilation . Utilisez GetType lorsque vous souhaitez obtenir le type lors de l' exécution . Il y a rarement des cas à utiliser comme dans le cas d'une distribution et, dans la plupart des cas, vous finissez par lancer la variable de toute façon.

Il y a une quatrième option que vous n'avez pas envisagée (surtout si vous allez lancer un objet sur le type que vous trouvez aussi); c'est 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;

nécessite deux .


Vous pouvez utiliser l'opérateur "typeof ()" en C # mais vous devez appeler l'espace de noms en utilisant System.IO; Vous devez utiliser le mot clé "is" si vous souhaitez vérifier un type.


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

Ceci est une erreur L'opérateur typeof en C # peut uniquement prendre des noms de type, pas des objets.

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

Cela fonctionnera, mais peut-être pas comme prévu. Pour les types de valeur, 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 exactement le même type, et non quelque chose d'autre dans la hiérarchie d'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");
}

Cela imprimerait "o is something else" , parce que le type de o est Dog , pas Animal . Vous pouvez cependant effectuer ce travail 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");

Cependant, cette technique laisse toujours un problème majeur. Si votre variable est null, l'appel à GetType() lancera une exception NullReferenceException. Donc, pour le faire fonctionner correctement, vous feriez:

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

Avec ceci, vous avez un comportement équivalent du mot is clé is . Par conséquent, si c'est le comportement que vous souhaitez, vous devez utiliser le mot is clé is , 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 is clé is n'est toujours pas ce que vous voulez vraiment, car il n'est généralement pas suffisant de savoir qu'un objet est d'un certain type. Habituellement, vous voulez réellement utiliser cet objet comme une instance de ce type, ce qui nécessite de le lancer aussi. Et ainsi vous pourriez vous retrouver à écrire du code comme ceci:

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

Mais cela fait que le CLR vérifie le type de l'objet jusqu'à deux fois. Il vérifie une fois pour satisfaire l'opérateur is , et si o est en effet un Animal , nous le faisons vérifier à nouveau pour valider le cast.

Il est plus efficace de le faire à la place:

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

L'opérateur as est une distribution qui ne lancera pas d'exception si elle échoue, mais retournera 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 lance pas d'exceptions, certaines personnes pensent qu'il s'agit d'un casting "sûr", et ils l'utilisent exclusivement, évitant les lancers 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 un Animal , et tant que son hypothèse est correcte, tout fonctionne bien. Mais s'ils ont tort, ils se retrouvent avec une NullReferenceException . Avec une distribution régulière, ils auraient obtenu une InvalidCastException 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();
    }
}

C'est un autre cas où le développeur s'attend à ce que o soit un Animal chaque fois, mais ce n'est pas évident dans le constructeur, où le cast est utilisé. Ce n'est pas évident jusqu'à ce que vous arriviez à la méthode Interact , où l'on s'attend à ce que le champ animal soit assigné positivement. Dans ce cas, non seulement vous vous retrouvez avec une exception trompeuse, mais elle n'est pas projetée avant que l'erreur réelle ne se produise.

En résumé:

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

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

  • Si vous devez traiter un objet en tant qu'instance d'un certain type et que l'objet est supposé être de ce type, utilisez une distribution standard.


if (c is UserControl) c.Enabled = enable;




c#