你能从一个MethodInfo对象得到一个Func <T>(或类似的)吗?

我认识到,一般来说,使用反思会有性能影响。 (实际上,我自己并不是一个反思的粉丝,这纯粹是一个学术问题。)

假设存在一些看起来像这样的类:

public class MyClass { public string GetName() { return "My Name"; } } 

忍受我在这里。 我知道,如果我有一个名为xMyClass的实例,我可以调用x.GetName() 。 此外,我可以设置一个Func<string>variables为x.GetName

这是我的问题。 假设我知道上面的类叫做MyClass ; 我有一些对象,但我不知道它是什么。 我可以通过执行以下操作检查该对象是否具有GetName方法:

 MethodInfo getName = x.GetType().GetMethod("GetName"); 

假设getName不为空。 然后我不能进一步检查是否getName.ReturnType == typeof(string)getName.GetParameters().Length == 0 ,在这一点上,我不会相当肯定,我的getName对象表示的方法可以肯定会被转换为Func<string> ,不知何故?

我意识到有一个MethodInfo.Invoke ,我也意识到我总是可以创build一个Func<string>如:

 Func<string> getNameFunc = () => getName.Invoke(x, null); 

我想我问的是,如果有任何方法一个MethodInfo对象它所表示的实际方法,招致反映在过程中的性能成本,但之后 ,能够直接调用该方法(通过,例如,一个Func<string>或类似的东西) 没有性能损失。

我所设想的可能是这样的:

 // obviously this would throw an exception if GetActualInstanceMethod returned // something that couldn't be cast to a Func<string> Func<string> getNameFunc = (Func<string>)getName.GetActualInstanceMethod(x); 

(我意识到这不存在,我想知道是否有类似的东西。)

这种取代了我以前的答案,因为这是一个稍微长一点的路线 – 给你一个快速的方法调用,而不像其他一些答案,允许你通过不同的实例(如果你要遇到多个实例相同types)。 如果你不想要,请查看底部的更新(或查看Ben M的答案)。

这是一个testing方法,可以做你想做的事情:

 public class TestType { public string GetName() { return "hello world!"; } } [TestMethod] public void TestMethod2() { object o = new TestType(); var input = Expression.Parameter(typeof(object), "input"); var method = o.GetType().GetMethod("GetName", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); //you should check for null *and* make sure the return type is string here. Assert.IsFalse(method == null && !method.ReturnType.Equals(typeof(string))); //now build a dynamic bit of code that does this: //(object o) => ((TestType)o).GetName(); Func<object, string> result = Expression.Lambda<Func<object, string>>( Expression.Call(Expression.Convert(input, o.GetType()), method), input).Compile(); string str = result(o); Assert.AreEqual("hello world!", str); } 

一旦构build委托一次 – 您可以将其caching在字典中:

 Dictionary<Type, Func<object, string>> _methods; 

你所做的就是把它添加到字典中,使用传入对象的types(来自GetType())作为关键字。 在将来,你首先检查是否在字典中有一个已经准备好的委托(如果可以的话调用它),否则你先构build它,然后添加它,然后调用它。

顺便提一句,这是DLR为其dynamic调度机制所做的一种非常高度简化的版本(使用C#术语,即使用“dynamic”关键字时)。

最后

如果像less数人提到的那样,你只是想直接将Func绑定到接收到的对象上,那么你可以这样做:

 [TestMethod] public void TestMethod3() { object o = new TestType(); var method = o.GetType().GetMethod("GetName", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); Assert.IsFalse(method == null && !method.ReturnType.Equals(typeof(string))); //this time, we bake Expression.Constant(o) in. Func<string> result = Expression.Lambda<Func<string>>( Expression.Call(Expression.Constant(o), method)).Compile(); string str = result(); //no parameter this time. Assert.AreEqual("hello world!", str); } 

但是请注意,一旦expression式树被抛弃,您需要确保o保持在范围内,否则您可能会得到一些令人讨厌的结果。 最简单的方法是在代理的整个生命周期中保留一个本地引用(也许在类实例中)。 (由于本M的评论而被删除)

是的,这是可能的:

 Func<string> func = (Func<string>) Delegate.CreateDelegate(typeof(Func<string>), getName); 

这是我的答案,通过构buildexpression式树。 与其他答案不同,结果( getNameFunc )是绑定到原始实例的函数 – 无需将其作为parameter passing。

 class Program { static void Main(string[] args) { var p = new Program(); var getNameFunc = GetStringReturningFunc(p, "GetName"); var name = getNameFunc(); Debug.Assert(name == p.GetName()); } public string GetName() { return "Bob"; } static Func<string> GetStringReturningFunc(object x, string methodName) { var methodInfo = x.GetType().GetMethod(methodName); if (methodInfo == null || methodInfo.ReturnType != typeof(string) || methodInfo.GetParameters().Length != 0) { throw new ArgumentException(); } var xRef = Expression.Constant(x); var callRef = Expression.Call(xRef, methodInfo); var lambda = (Expression<Func<string>>)Expression.Lambda(callRef); return lambda.Compile(); } } 

最简单的方法是通过Delegate.CreateDelegate

 Func<string> getNameFunc = (Func<string>) Delegate.CreateDelegate( typeof(Func<string>), x, getName); 

请注意,这将getNameFunc绑定到x ,因此对于每个x您都需要创build一个新的委托实例。 这个选项比基于Expression的例子复杂得多。 但是,使用基于expression式的示例,可以创build一次Func<MyClass, string> getNameFuncForAny ,您可以为MyClass每个实例重新使用该函数。

要创build这样一个getNameFuncForAny,你需要一个类似的方法

 public Func<MyClass, string> GetInstanceMethod(MethodInfo method) { ParameterExpression x = Expression.Parameter(typeof(MyClass), "it"); return Expression.Lambda<Func<MyClass, string>>( Expression.Call(x, method), x).Compile(); } 

你可以这样使用:

 Func<MyClass, string> getNameFuncForAny = GetInstanceMethod(getName); MyClass x1 = new MyClass(); MyClass x2 = new MyClass(); string result1 = getNameFuncForAny(x1); string result2 = getNameFuncForAny(x2); 

如果你不想绑定到Func<MyClass, string> ,你可以定义

 public TDelegate GetParameterlessInstanceMethod<TDelegate>(MethodInfo method) { ParameterExpression x = Expression.Parameter(method.ReflectedType, "it"); return Expression.Lambda<TDelegate>( Expression.Call(x, method), x).Compile(); } 

你可以build立一个表示调用这个方法的lambdaexpression式的expression式树,然后Compile()它,这样进一步的调用就像标准的编译调用一样快。

或者,我很早以前就写了这个方法 ,基于一个很好的MSDN文章,它使用IL来产生一个包装,以比MethodInfo.DynamicInvoke更快的方式调用任何MethodInfo方法,因为一旦生成代码,几乎没有正常调用的开销。

我头上的方法之一就是使用dynamic。 那么你可以这样:

 if( /* This method can be a Func<string> */) { dynamic methodCall = myObject; string response = methodCall.GetName(); }