CSharp - 如何使用反射来调用泛型Method ?

  显示原文与译文双语对照的内容

当类型参数在编译时未知,但在运行时动态获取时,调用泛型方法的最佳方法是什么?

考虑下面的示例代码- 在 Example() 方法中,使用存储在myType变量中的类型来调用 GenericMethod() 是最简单的方法?


public class Sample
{

 public void Example(string typeName)
 {
 Type myType = FindType(typeName);

//what goes here to call GenericMethod<T>()? 
 GenericMethod<myType>();//This doesn't work

//what changes to call StaticMethod<T>()?
 Sample.StaticMethod<myType>();//This also doesn't work
 }

 public void GenericMethod<T>()
 { 
. . .
 }

 public static void StaticMethod<T>()
 { 
. . .
 } 
}

时间:

你需要使用反射来获取该方法开始时,再用"构造"它通过提供类型参数和 MakeGenericMethod:


MethodInfo method = typeof(Sample).GetMethod("GenericMethod");
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

对于静态方法,通过 null 作为 Invoke的第一个参数。 这与一般方法无关- 它只是普通的反射。

如前所述,则许多这类更为简单的钟表 C# 4使用 dynamic - 如果你可以使用类型推断,当然。 对于类型推断不可用的情况,如问题中的确切示例,它没有帮助。

只是对原始答案的补充。 虽然这将起作用:


MethodInfo method = typeof(Sample).GetMethod("GenericMethod");
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

你丢失compile-time检查GenericMethod也有点危险。 如果你以后进行重构并重命名 GenericMethod,这里代码将不会通知,并且在运行时将失败。 而且,如果有任何程序集的post-processing ( 例如混淆或者删除未使用的方法/类) 这段代码也可能失效。

因此,如果你知道在编译时要链接到的方法,而这又不是上百万次,那么开销无关紧要,我将更改这里代码为:


Action<> GenMethod = GenericMethod<int>;//change int by any base type 
//accepted by GenericMethod
MethodInfo method = this.GetType().GetMethod(GenMethod.Method.Name);
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

虽然不是非常漂亮,你有一个编译时对GenericMethod引用在这里,如果你用GenericMethod重构,删除或者做任何事情,这段代码在编译时将保持连续工作,或者至少停歇( 例如你删除 GenericMethod ) 。

另一种方法是创建一个新的包装类,并通过Activator创建它。 我不知道是否有更好的方法。

这与我在另一个星期问的问题相同: 反射和泛型类型

然后我介绍了如何在我的博客中调用一般重载方法: http://www.aaron-powell.com/reflection-and-generics

由于 C# 可以使用运行时类型调用,所以 4.0反射不是必需的。 于you,相关的,现在用clr库的开源框架是件很痛苦的事动态( 而不是为你生成代码的C# 编译器),Dynamitey ( PCL ) 让你轻松缓存的运行时编译器便建立需要访问相同的调用


var name = InvokeMemberName.Create;
Dynamic.InvokeMemberAction(this, name("GenericMethod", new[]{myType}));


var staticContext = InvokeContext.CreateStatic;
Dynamic.InvokeMemberAction(staticContext(typeof(Sample)), name("StaticMethod", new[]{myType}));

添加到 @adrian gallero答案:

从类型信息调用泛型方法涉及 3个步骤。

TLDR: 使用类型对象调用已知的泛型方法可以通过以下方法完成:


 ((Action)GenericMethod<object>)
. Method
. GetGenericMethodDefinition()
. MakeGenericMethod(typeof(string))
. Invoke(this, null);

其中 GenericMethod<object> 是调用的方法名称和满足泛型约束的任何类型。

( 操作) 匹配要被称为 换句话说,( Func<string,string,int> 或者 Action<bool> )的方法的签名

步骤 1正在获取泛型方法定义的MethodInfo

方法 1: 使用具有适当类型或者绑定标志的GetMethod() 或者 GetMethods() 。


MethodInfo method = typeof(Sample).GetMethod("GenericMethod");

方法 2: 创建一个委托,获取MethodInfo对象,然后调用 GetGenericMethodDefinition

从包含方法的类中:


MethodInfo method = ((Action)GenericMethod<object>)
. Method
. GetGenericMethodDefinition();

MethodInfo method = ((Action)StaticMethod<object>)
. Method
. GetGenericMethodDefinition();

从包含方法的类外部:


MethodInfo method = ((Action)(new Sample())
. GenericMethod<object>)
. Method
. GetGenericMethodDefinition();

MethodInfo method = ((Action)Sample.StaticMethod<object>)
. Method
. GetGenericMethodDefinition();

在 C# 中,方法,换句话说,"tostring"或者"GenericMethod"的名称实际上是指一组方法,这些方法可能包含一个或者多个方法。 在提供方法参数的类型之前,不知道所引用的方法。

((Action)GenericMethod<object>) 引用特定方法的委托。 ((Func<string, int>)GenericMethod<object>) 指GenericMethod的不同重载

方法 3: 创建包含方法调用表达式的lambda表达式,获取MethodInfo对象,然后


MethodInfo method = ((MethodCallExpression)((Expression<Action<Sample>>)(
 (Sample v) => v.GenericMethod<object>()
 )).Body).Method.GetGenericMethodDefinition();

这将分解为

创建一个lambda表达式,其中主体是对所需方法的调用。


Expression<Action<Sample>> expr = (Sample v) => v.GenericMethod<object>();

提取出正文和强制转换为 MethodCallExpression


MethodCallExpression methodCallExpr = (MethodCallExpression)expr.Body;

从方法获取泛型方法定义


MethodInfo methodA = methodCallExpr.Method.GetGenericMethodDefinition();

步骤 2正在调用MakeGenericMethod以创建具有适当 type(s)的泛型方法。


MethodInfo generic = method.MakeGenericMethod(myType);

步骤 3正在调用具有适当参数的方法。


generic.Invoke(this, null);

...