私は多くの人が以下のコードを使うのを見ました:
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
個人的には、最後のものが一番きれいだと感じますが、足りないものはありますか?どちらを使うのが一番良いですか、それとも個人的な好みですか?
すべて違います。
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"
コンパイル時 に型を取得したい場合は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 が必要です。
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というインタフェースを実装している場合に当てはまります。
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
を確認してください。
オブジェクトを特定の型のインスタンスとして扱う必要があり、そのオブジェクトがその型であると想定される場合は、通常のキャストを使用してください。
私はType
プロパティを比較するための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
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のコメントに従ってスイッチを使用するためのより長い新しい方法を更新しました。
私は好む は
とはいえ、使用している場合 は、おそらく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
}
}
私は最後のものも継承を検討していると思います(例:Dog is Animal == true)。これはほとんどの場合より優れています。
それは私がしていることによります。ブール値が必要な場合(たとえば、intにキャストするかどうかを判断するため)、is
を使用します。何らかの理由で実際にその型が必要な場合(たとえば、他のメソッドに渡すために)、GetType()
を使用します。
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
*/
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
*/
最後のものはより明確で、より明白で、そしてまたサブタイプをチェックします。他のものは多型をチェックしません。