web-dev-qa-db-ja.com

C#で反映された型にキャストする

次のコードを検討してください。

object objFoo = MakeFoo(); // object MakeFoo(){return new Foo();}
MethodInfo methodInfo = typeof(Program).GetMethod("Baz"); // Foo Baz(){return foo;}
Type typeFoo = methodInfo.ReturnType;
var result = (typeFoo)objFoo;

結果を得るためにtypeFooで魔法をかける必要がありますか?

17
user2341923

番号 :-)

ケース1:

object objFoo = MakeFoo(); // object MakeFoo(){return new Foo();}
Foo result = (Foo)objFoo;

コンパイル時にFoo型を知っているため、ここではリフレクションはありません。

ケース2:インターフェース。通常は最高のものです... MakeFooが何を返すのか正確にはわかりませんが、それはIFooインターフェースであることは知っています...

object objFoo = MakeFoo(); // object MakeFoo(){return new Foo();}
IFoo result = (IFoo)objFoo;

ケース3:MakeFooFooを返すかどうかわからない

object objFoo = MakeFoo(); // object MakeFoo(){return new Foo();}

if (objFoo is Foo)
{
    Foo result = (Foo)objFoo;
}

または類似

object objFoo = MakeFoo(); // object MakeFoo(){return new Foo();}

Foo foo = objFoo as Foo;

if (foo != null)
{
    // use foo
}

ケース4:タイプFooはプログラムでは完全に不明です。参照可能なFooクラスがありません...

object objFoo = MakeFoo(); // object MakeFoo(){return new Foo();}
Type typeFoo = objFoo.GetType(); // You should check for null values before!

// and now?

dynamic foo = objFoo;

// because you know that foo can Quack(1, 2, 3)!
string result = foo.Quack(1, 2, 3); 

// note that it will explode with a RuntimeBinderException if there is no 
// string Quack(int, int, int) method!

dynamicは内部的にリフレクションを使用します。リフレクションを直接使用してQuackメソッドを取得し、それを呼び出すことができます

ケース5:ケース4と同じですが、直接反射を使用します。

object objFoo = MakeFoo(); // object MakeFoo(){return new Foo();}
Type typeFoo = objFoo.GetType(); // You should check for null values before!
MethodInfo mi = type.GetMethod("Quack"); // You should check if the Quack method
                                         // exists
string result = (string)mi.Invoke(objFoo, new object[] { 1, 2, 3 });

または、いくつかの健全性チェックで、fooQuackを正しく実行できるか不明な場合:

MethodInfo mi = type.GetMethod("Quack", 
                    BindingFlags.Instance | BindingFlags.Public, 
                    null, 
                    new[] { typeof(int), typeof(int), typeof(int) }, 
                    null);

if (mi != null && typeof(string).IsAssignableFrom(mi.ReturnType))
{
    string result = (string)mi.Invoke(objFoo, new object[] { 1, 2, 3 });
}

Case -Infinity:タイプFooは、プログラムでは完全に不明です。参照可能なFooクラスがありません。 IFooインターフェースがありません。あなたはFooが何であるかさえ知りません、あなたはそれがクラスであることだけを知っています(あるいはそれは箱入りのstructであるかもしれません、しかしそれはあなたの観点から変化しません...それはinterfaceではあり得ないので、すべてのclassの後ろには、具体的なstruct/interfaceが常に存在する必要があります。メソッド、フィールド、プロパティがわかりません(Fooが何であるかわからないため)。

objectをこの不明なクラスにキャストできたとしても、何ができますか?パラメータ/戻り値としてそれを受け入れるメソッドをコードに含めることはできません。

int INeedFoo(Foo par) { return 0; }

そうすれば、Fooがわかるでしょう。 .NETライブラリには、それをパラメーター/戻り値として受け入れるメソッドを含めることはできません。それが可能であれば、Fooを知っているからです。

あなたができる唯一のことは、パラメータとしてFooを受け入れるリフレクションを通じて発見した他のいくつかのメソッドにそれを渡すことです...しかし、Invokeメソッドは、objectの配列をパラメータとして受け入れます... objectをキャストする必要はありませんInvokeを呼び出す!配列に入れるだけです。

55
xanatos

これは、反射型へのキャストに関するGoogleの最初の結果です。

したがって、参考までに、sbがリフレクションタイプにキャストする一般的な方法は何だろうと思う場合は、次のようにします。

public static class ObjectExtensions
{
    public static T CastTo<T>(this object o) => (T)o;

    public static dynamic CastToReflected(this object o, Type type)
    {
        var methodInfo = typeof(ObjectExtensions).GetMethod(nameof(CastTo), BindingFlags.Static | BindingFlags.Public);
        var genericArguments = new[] { type };
        var genericMethodInfo = methodInfo?.MakeGenericMethod(genericArguments);
        return genericMethodInfo?.Invoke(null, new[] { o });
    }
}
2
rvnlord

これは次と同等です。

object objFoo = MakeFoo();
Foo result = (Foo)objFoo;

コンパイル時に不明な型にオブジェクトをキャストしても、実際には意味がありません-使用できません。

object objFoo = MakeFoo();
UnkownType result = (UknownType)objFoo;

UknownTypeが何であるかがわからないため、リフレクションやダイナミクスに頼らずにそのメソッドを使用することはできません。

1
zmbq