型チェック:typeof、GetType、またはis?


1513

多くの人が次のコードを使用するのを見てきました。

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

個人的には最後が一番綺麗だと思いますが、足りないものはありますか?どちらが最適ですか、それとも個人的な好みですか?


28
忘れずにas
RCIX、2009年

82
asしかし、実際には型チェックではありません...
jasonh

49
as確かに、型チェックの1つの形式isです。それisは舞台裏で効果的に使用され、MSDN内の至る所で、コードのクリーン度を向上させる場所で使用されますisis最初にチェックする代わりにas、使用の準備が整った型付き変数を確立するための呼び出し:nullの場合、適切に応答します。それ以外の場合は続行します。確かに、私が見たり使用したりしたことがあるものです。
ザッコーネ2014

15
賛成で大幅なパフォーマンスの差があるas/ is(でカバーstackoverflow.com/a/27813381/477420あなたのケースのためにその意味的な作品を仮定)が。
Alexei Levenkov、2015年

@samusarinリフレクションを「使用」しません。GetTypeあなたがにリンクされている方法がある中でSystem.Reflection.Assembly、ここで完全に異なる方法とは無関係- 。
Kirk Woll 2017

回答:


1849

すべてが異なります。

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

29
ああ、だから、Carから派生したFordクラスとFordのインスタンスがある場合、そのインスタンスで「is Car」をチェックするとtrueになります。理にかなっています!
jasonh

2
明確にするために、私はそれを知っていましたが、コードサンプルを追加する前にコメントしました。私はあなたのすでに素晴らしい答えにいくつかの明白な英語の明快さを加えたいと思いました。
jasonh 2009年

12
@Shimmy typeofがコンパイル時に評価され、GetType()が実行時に評価される場合、GetType()がわずかなパフォーマンスヒットを引き起こすことは理にかなっています
Cedric Mamo

new Dog()。GetType()がAnimalであるか、typeof(Dog)がAnimalである場合は、警告ではなくエラーが表示されますか?
Prera​​k K 2014

7
@Prera​​kKは、new Dog().GetType() is Animal以来、偽(とあなたの他のバージョンも)を返す.GetType()リターン型のオブジェクトをType、とTypeではありませんAnimal
Maarten

195

コンパイル時にtypeof型を取得したい場合に使用します。実行時に型を取得したい場合に使用します。キャストのように使用するケースはほとんどなく、ほとんどの場合、とにかく変数をキャストすることになります。GetTypeis

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

Foo foo = obj as Foo;

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

これは1つのキャストのみを使用しますが、このアプローチでは:

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

2が必要です。

アップデート(2020年1月):

  • C#7+以降、インラインでキャストできるようになったため、「is」アプローチを1つのキャストでも実行できるようになりました。

例:

if(obj is Foo newLocalFoo)
{
    // For example, you can now reference 'newLocalFoo' in this local scope
    Console.WriteLine(newLocalFoo);
}

4
.NET 4の変更により、isキャストは実行されますか?
ahsteele 2013年

6
この答えは正しいですか?typeof()にインスタンスを本当に渡すことができるのは本当ですか?私の経験はそうではありませんでした。しかし、インスタンスのチェックは実行時に行わなければならない可能性があるのに対し、クラスのチェックはコンパイル時に実行できるはずであるのは、一般的に正しいと思います。
Jon Coombs 2013年

4
@jon(qの4年後)、いいえ、インスタンスをtypeof()に渡すことはできません。この答えは、そうすることを示唆していません。代わりに型を渡します。つまり、typeof(string)機能しますがtypeof("foo")機能しません。
アベル

isILでキャストを行うのではなく、特別な操作を行うとは思いません。
abatishchev 2018年

3
私たちは、今できることif (obj is Foo foo) { /* use foo here */ }
イワン・ガルシアTopete

71

1。

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

typeof変数ではなく型でのみ機能するため、これは違法です。obj1は変数であると想定しています。したがって、この方法typeofは静的であり、実行時ではなくコンパイル時に機能します。

2。

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

これは、あるtrue場合にはobj1、正確にタイプのですint。場合はobj1派生からint、もし条件になりますfalse

3。

if (obj1 is int)

これはtrueobj1である場合int、またはと呼ばれるクラスから派生するint場合、またはと呼ばれるインターフェイスを実装する場合intです。


1について考えると、その通りです。それでも、ここのいくつかのコードサンプルで見ました。Type t = obj1.GetType();である必要があります。
jasonh 2009年

4
そうだと思います。「typeof(obj1)」を試してもコンパイルされません。
スコットランガム、

4
System.Int32またはC#の他の値の型から派生させることは不可能です
reggaeguitar

typeof(typeof(system.int32))がどうなるかわかりますか
サナ

1
@Sana、それを試してみませんか:)タイプSystem.Typeを表すSystem.Typeのインスタンスが返されると思いますが、typeof演算のためのドキュメントはこちらです:docs.microsoft.com/en-us/dotnet/csharp/language-reference/...
スコットランガム

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

これはエラーです。C#のtypeof演算子は、オブジェクトではなく型名のみを使用できます。

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

これは機能しますが、おそらく期待どおりではありません。ここで示したように、値の型の場合は許容されますが、参照型の場合は、型が完全に同じ型であり、継承階層の他のものではない場合にのみ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 is something else"の種類があるため、oあるDog、ありませんAnimal。ただし、クラスの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演算子を満足させるために1回チェックし、o実際にである場合はAnimal、キャストを検証するためにもう一度チェックします。

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

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

asオペレータは、それが代わりに返し、失敗した場合に例外をスローしませんキャストですnull。このように、CLRはオブジェクトの型を1回だけチェックし、その後は、より効率的なnullチェックを実行するだけで済みます。

ただし、注意してください。多くの人がで罠に陥りasます。例外をスローしないため、一部の人々はそれを「安全な」キャストと見なし、通常のキャストを避けて排他的に使用します。これにより、次のようなエラーが発生します。

(o as Animal).Speak();

この場合、開発者はそれo常にであることを明確に想定してAnimalおり、その想定が正しい限り、すべてが正常に機能します。しかし、それらが間違っている場合、それらがここで終わるのはNullReferenceExceptionです。通常のキャストでは、InvalidCastException代わりに彼らが問題をより正確に特定していたはずです。

時々、このバグを見つけるのは難しいかもしれません:

class Foo{
    readonly Animal animal;

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

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

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

要約すれば:

  • オブジェクトがなんらかのタイプかどうかを知る必要があるだけなら、を使用してくださいis

  • オブジェクトを特定のタイプのインスタンスとして扱う必要があるが、オブジェクトがそのタイプであるかどうかがわからない場合は、を使用asして確認しnullます。

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


if(o is Animal)((Animal)o).Speak();これの何が問題になっていますか ?詳細を教えてください。
batmaci 2017年

2
@batmaci:答えの中にあります-2つの型チェックを引き起こします。初回はo is Animalであり、CLRは変数の型がであるかどうかを確認する必要oがありAnimalます。2番目にチェックするのは、ステートメントでキャストするとき((Animal)o).Speak()です。2回チェックするのではなく、を使用して1回チェックしasます。
siride

わかりやすく説明していただき、ありがとうございます。
Paul Efford

16

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

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のコメントに従ってスイッチを使用するように、より長い新しい方法を更新しました。


1
この場合switch、パターンマッチングでステートメントを使用することをお勧めします。
Palec

あなたはどのように対処しますか?この特定のコードブロックでは?if (obj1 is int integerValue) { integerValue++; }
ベン

ベン、私があなたの質問を理解していれば、整数変数に非整数を入れることができないので、他のケースを処理するためのelseステートメントを持っているでしょう。:)
JoelC

14

私が持っていたTypeと比較する-propertyをして使用することができませんでしis(のようなmy_type is _BaseTypetoLookFor)が、私はこれらを使用することができます。

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

お知らせIsInstanceOfTypeおよびIsAssignableFrom戻り値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

9

私が好むの

つまり、を使用している場合、継承を適切に使用していない可能性があります。

その人:エンティティ、その動物:エンティティを想定します。フィードはエンティティの仮想メソッドです(ニールを幸せにするため)

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

1
確かに、PersonがAnimalから派生していることを知って、私は前者を決して実行しません。
jasonh 2009年

3
後者も実際には継承を使用していません。Fooは、PersonおよびAnimalでオーバーライドされるエンティティの仮想メソッドである必要があります。
ニールウィリアムズ

2
@bobobobo「継承」ではなく「オーバーロード」を意味すると思います。
lc。

@lc:いいえ、私は継承を意味します。最初の例は、さまざまな動作を行うための一種の誤った方法です(isを使用)。2番目の例では、オーバーロードyesを使用していますが、isの使用は避けています。
bobobobo 2009年

1
この例の問題は、スケーリングできないことです。食べる必要のある新しいエンティティ(昆虫やモンスターなど)を追加した場合、エンティティクラスに新しいメソッドを追加し、それを供給するサブクラスでそれをオーバーライドする必要があります。これは、(エンティティがX)である場合(エンティティがY)である場合、リストよりも望ましいことではありません...これは、LSPおよびOCPに違反しています。何らかの形の委任がおそらく推奨されます。
エブラウン09年

5

最後の1つは継承も検討していると思います(たとえば、Dog is Animal == true)。これはほとんどの場合に優れています。


2

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


1
いい視点ね。ifステートメントを使用して型をチェックするいくつかの回答を調べた後、この質問にたどり着いたことを忘れていました。
jasonh 2009年

0

最後のものはより明確で、より明白で、サブタイプもチェックします。他の人はポリモーフィズムをチェックしません。


0

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

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

4
詳細を編集してください。コードのみと「これを試す」の回答は、検索可能なコンテンツが含まれておらず、誰かが「これを試す」必要がある理由を説明していないため、お勧めしません。
abarisone 2016

あなたの答えは質問とは無関係です。
menxin

-5

C#では "typeof()"演算子を使用できますが、System.IOを使用して名前空間を呼び出す必要があります。タイプを確認する場合は、「is」キーワードを使用する必要があります。


7
typeofは名前空間で定義されておらず、キーワードです。System.IOこれとは何の関係もありません。
Arturo TorresSánchez15年

-5

パフォーマンステストのtypeof()と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();
    }
}

デバッグモードの結果:

00:00:08.4096636
00:00:10.8570657

リリースモードでの結果:

00:00:02.3799048
00:00:07.1797128

1
パフォーマンス測定にDateTime.UtcNowを使用しないでください。あなたのコードでストップウォッチクラスを使用すると、デバッグモードで永続的に反対の結果が得られました。UseTypeOf:00:00:14.5074469 UseGetType:00:00:10.5799534。リリースモードは同じですが、予想通りです
Alexey Shcherbak 2015年

@AlexeyShcherbak StopwatchとDateTime.Nowの差は10〜20ミリ秒を超えることはできません。コードをもう一度確認してください。また、テストではミリ秒は気にしません。また、ストップウォッチを使用すると、コードが数行長くなります。
Alexander Vasilyev 2015年

1
これは、特定のケースではなく、一般に悪い習慣です。
Alexey Shcherbak 2015年

4
@AlexanderVasilyev 文書で誤解を招くようなことを行うための引数としてコード行を使用しないでください。msdn.microsoft.com/en-us/library/system.datetime(v=vs.110).aspxに示されDateTimeているように、100ms未満の時間を心配する場合は、OSのタイムフレームを使用するため、使用しないでくださいStopwatchプロセッサを使用すると比較すると、Win7でがTick使用する解像度DateTimeは百ミリ秒です。
Eric Wu
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.