web-dev-qa-db-ja.com

型チェック: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

個人的には、最後のものが一番きれいだと感じますが、足りないものはありますか?どちらを使うのが一番良いですか、それとも個人的な好みですか?

1351
jasonh

すべて違います。

  • 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"
1663
Jimmy

コンパイル時 に型を取得したい場合はtypeofを使用してください。 実行時 に型を取得したい場合はGetTypeを使用してください。キャストを行うときにisを使用するケースはめったにありません。ほとんどの場合、とにかく変数をキャストすることになります。

考慮していない4つ目のオプションがあります(特に、オブジェクトをあなたが見つけたタイプにキャストしようとしている場合)。それはasを使うことです。

Foo foo = obj as Foo;

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

これは one castのみを使用しますが、このアプローチは次のとおりです。

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

two が必要です。

177
Andrew Hare

1.

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

Typeofは型に対してのみ機能し、変数に対しては機能しないため、これは不正です。 obj1が変数だと思います。したがって、このようにしてtypeofは静的であり、実行時ではなくコンパイル時に機能します。

2.

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

Obj1が正確にint型であれば、これは当てはまります。 obj1がintから派生している場合、if条件はfalseになります。

3.

if (obj1 is int)

これは、obj1がintの場合、またはintというクラスから派生している場合、あるいはintというインタフェースを実装している場合に当てはまります。

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

これはエラーです。 C#のtypeof演算子は型名のみを取り、オブジェクトは取りません。

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

これはうまくいくでしょうが、あなたが期待していたのとは違うかもしれません。ここで示したように、値型の場合は許容されますが、参照型の場合は、型が まったく同じ typeである場合にのみ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の型はDogではなくAnimalであるため、これは"o is something else"を出力します。ただし、IsAssignableFromクラスのTypeメソッドを使用すれば、これを機能させることができます。

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

しかしながら、この技術は依然として大きな問題を残している。変数がnullの場合、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にオブジェクトのタイプを2回までチェックさせます。それはis演算子を満たすためにそれを一度チェックします、そしてoが本当にAnimalであるならば、我々はキャストを検証するためにそれを再びチェックさせます。

代わりにこれを行う方が効率的です。

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

as演算子は、失敗しても例外をスローせず、代わりにnullを返すキャストです。このようにして、CLRはオブジェクトの型を一度だけチェックし、その後、nullチェックを実行するだけで済みます。これはより効率的です。

しかし用心しなさい:多くの人々はasで罠に陥る。例外をスローしないため、「安全な」キャストとみなし、排他的に使用して通常のキャストを避けます。これは次のようなエラーにつながります。

(o as Animal).Speak();

この場合、開発者はo always Animalになると明確に仮定しており、その仮定が正しい限り、すべてうまくいきます。しかし、もし彼らが間違っていたら、それで彼らがここで終わるのはNullReferenceExceptionです。通常のキャストでは、彼らは代わりにInvalidCastExceptionを得たでしょう、そしてそれは問題をより正確に識別したでしょう。

時々、このバグは見つけるのが難しい場合があります:

class Foo{
    readonly Animal animal;

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

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

これはoが毎回Animalであることを開発者が明確に期待しているもう1つのケースですが、これはasキャストが使用されるコンストラクタでは明らかではありません。 Interactフィールドが積極的に割り当てられることが期待されているanimalメソッドに到達するまでは明らかではありません。この場合、誤解を招くような例外が発生するだけでなく、実際のエラーが発生したときよりもはるかに遅くなるまでスローされません。

要約すれば:

  • オブジェクトが何らかの型かどうかだけを知りたい場合は、isを使用してください。

  • オブジェクトを特定の型のインスタンスとして扱う必要があるが、そのオブジェクトがその型であることが確実でない場合は、asを使用してnullを確認してください。

  • オブジェクトを特定の型のインスタンスとして扱う必要があり、そのオブジェクトがその型であると想定される場合は、通常のキャストを使用してください。

46
P Daddy

私はTypeプロパティを比較するためのis-プロパティを持っていて、使うことができませんでした(my_type is _BaseTypetoLookForのように)が、私はこれらを使うことができました:

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

IsInstanceOfTypeIsAssignableFromが同じ型を比較す​​るとtrueを返すことに注意してください。ここで、IsSubClassOfはfalseを返します。そしてIsSubclassOfは、他の2つがそうであるように、インターフェースには働きません。 ( この質問と回答 も参照してください。)

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

C#7を使用しているのであれば、Andrew Hareの素晴らしい答えを更新する時が来ました。 パターンマッチング は、ifステートメントのコンテキスト内で型指定された変数を提供するNiceショートカットを導入しました。別の宣言/キャストおよびチェックを必要としません。

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

これは、このような単一のキャストにとっては非常に圧倒されているように見えますが、あなたのルーチンにたくさんの可能な型が入ってくると本当に輝きます。以下は、2回キャストを避けるための古い方法です。

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

編集:Palecのコメントに従ってスイッチを使用するためのより長い新しい方法を更新しました。

11
JoelC

私は好む 

とはいえ、使用している場合 、おそらくnot継承を適切に使用しています。

そのPerson:Entity、およびそのAnimal: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
bobobobo

私は最後のものも継承を検討していると思います(例:Dog is Animal == true)。これはほとんどの場合より優れています。

5

それは私がしていることによります。ブール値が必要な場合(たとえば、intにキャストするかどうかを判断するため)、isを使用します。何らかの理由で実際にその型が必要な場合(たとえば、他のメソッドに渡すために)、GetType()を使用します。

2
AllenG

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

最後のものはより明確で、より明白で、そしてまたサブタイプをチェックします。他のものは多型をチェックしません。

0
thecoop