Подтвердить что ты не робот

Приведение к отраженному типу в С#

Рассмотрим следующий код:

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, чтобы получить результат?

4b9b3361

Ответ 1

Нет: -)

Случай 1:

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

Здесь нет никакого отражения, потому что вы знаете тип Foo во время компиляции.

Интерфейс Case 2:. Обычно лучший... Вы не знаете, что именно возвращает MakeFoo, но вы знаете его как интерфейс IFoo...

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

Случай 3: вы не уверены MakeFoo возвращает Foo

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!

the 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 });

или, с некоторыми проверками здравомыслия, если вы не уверены, что Foo может Quack правильно:

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: type Foo полностью неизвестен вашей программе. Вы не используете класс Foo. У вас нет интерфейса IFoo. Вы даже не знаете, что такое Foo, вы знаете только то, что это класс (или, возможно, это boxed struct, но он не меняется с вашей точки зрения... Это не может быть interface, потому что в конце всегда должен быть конкретный class/struct за каждым interface). Вы не знаете его методов, полей, свойств (потому что вы не знаете, что такое Foo).

Даже если вы можете применить object к этому неизвестному классу, что вы можете сделать? Вы не можете иметь методы в своем коде, которые принимают его как значение параметра/возвращаемого значения, потому что если где-то у вас было:

int INeedFoo(Foo par) { return 0; }

то ясно, что вы знаете о Foo. Библиотека .NET не может иметь методы, которые принимают ее как значение параметра/возвращаемого значения, потому что если бы это было, вы знали бы о Foo.

Единственное, что вы можете сделать, это передать его другим методам, которые вы обнаружите через отражение, которые принимают Foo как параметр... Но метод Invoke принимает массив object в качестве параметров... Вам не нужно набрасывать object на вызов Invoke! Вам нужно только поместить его в массив.

Ответ 2

Это будет эквивалентно:

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

Нет реальной точки в том, что вы бросаете объект в тип, который неизвестен во время компиляции - вы не сможете его использовать:

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

Поскольку вы не знаете, что такое UknownType, вы не сможете использовать какие-либо его методы, не прибегая к отражению или динамике.

Ответ 3

Это первый результат в Google о приведении к отраженному типу.

Так что для справки, если sb задается вопросом, каков будет общий способ приведения к отраженному типу:

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