在C#中将类型转换为反射类型

29

请考虑以下代码:

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 上施加一些魔法才能得到结果吗?


你能解释一下,在 Foo result 之后你想要做什么吗? - Satish Bejgum
在原始代码中,它大概是这样的: MethodInfo methodInfo = ... typeFoo = methodInfo.ReturnType; 所以我不知道它会是哪种类型。 - user2341923
2
如果你不知道它的类型,那么如何在左侧声明变量? - Damien_The_Unbeliever
3个回答

68

不 :-)

案例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:你不确定 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
}

第四种情况:类型为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 方法并调用它。

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

情况-Infinity: 类型Foo完全未知于您的程序。您无法引用Foo类。您也没有IFoo接口。您甚至不知道Foo是什么,您只知道它是一个类(或者可能是一个装箱的struct,但从您的角度来看它并没有改变...它不能是一个interface,因为最终每个interface都必须有一个具体的class/struct支持)。您不知道其方法、字段和属性(因为您不知道Foo是什么)。

即使您可以将一个object转换为此未知类,您能做什么呢?您不能在代码中编写接受它作为参数/返回值的方法,因为如果某处您写了:

int INeedFoo(Foo par) { return 0; }

那么显然你应该知道 Foo。.NET库不能有接受它作为参数/返回值的方法,因为如果有的话,你就会知道 Foo

你唯一能做的就是将它传递给一些通过反射发现接受Foo作为参数的其他方法... 但是Invoke方法接受一个object数组作为参数... 你不需要将你的object强制转换来调用Invoke!你只需要将它放在数组中即可。


2
我把问题过于简化了 :-) - user2341923
@XyiTebe 告诉我你感兴趣的三种情况中哪一种。 - xanatos
1
@XyiTebe,你可以使用Convert.ChangeType将类型转换为在运行时已知的类型,但是对于强制转换,你可以(通过一些技巧)实现,但是你不会有一个正确类型的变量来存储该类型。如果您在编写代码时不知道Foo,则无法拥有Foo myFoo(除了通过表达式树和自动生成代码可能实现的某些自生成代码)... 但是最终,即使您拥有一个您不知道任何信息的Foo变量,您会怎么做?您如何知道您不知道的类型的方法?所有反射方法都接受object作为参数并返回object - xanatos
是的,谢谢您。dynamicConvert.ChangeType 这两个想法都很不错。 您说得对,动态类型可能是最好的选择,因为声明 Foo result 意味着在编译时已知 Foo 类型,而 obj result 无论如何都会给出对象。这似乎是静态类型语言的一种限制。 - user2341923
@XyiTebe Convert.ChangeType 只有在需要从数字类型(例如 int 转换为 longint 转换为 float 等)时才真正有用。第三种方法是使用 ToString()... 有时您真正需要的只是一个 string 描述 :-) Console.Write() 和许多其他方法只需使用 ToString() 来显示参数的内容。 - xanatos
显示剩余2条评论

14
这是谷歌搜索中与“将类型转换为反射类型”相关的第一个结果。
因此,供参考,如果有人想知道将类型转换为反射类型的一般方法是什么:
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 });
    }
}

ObjectExtensions 是从哪里来的?.NET Core 3好像没有这个类。 - TheConstructor
1
我也不喜欢缺乏明确性,感谢指出。ObjectExtensions是这两个方法所在的示例静态类。CastToReflected 对于测试或临时解决方案很有用,但请注意,在现实世界的情景中很少使用它是一个好主意,祝好运。 - rvnlord

1
这将相当于:

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

在编译时未知类型的对象进行类型转换没有实际意义 - 你将无法使用它:

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

由于您不知道 UknownType 是什么,因此您将无法使用它的任何方法,除非采用反射或动态方式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接