您可以从MethodInfo对象中获得func (或类似)吗?
-
05-10-2019 - |
题
我意识到,总的来说,使用反射有绩效含义。 (实际上,我本人根本不喜欢反思;这是一个纯粹的学术问题。)
假设有一些看起来像这样的课程:
public class MyClass {
public string GetName() {
return "My Name";
}
}
在这里和我在一起。我知道,如果我有一个实例 MyClass
叫 x
, , 我可以打电话 x.GetName()
. 。此外,我可以设置一个 Func<string>
变量 x.GetName
.
现在这是我的问题。假设我 别 知道上一类称为 MyClass
;我有一些对象, x
, ,但我不知道这是什么。我可以检查一下该对象是否具有 GetName
这样做的方法:
MethodInfo getName = x.GetType().GetMethod("GetName");
认为 getName
不是零。那我不能进一步检查是否 getName.ReturnType == typeof(string)
和 getName.GetParameters().Length == 0
, ,在这一点上,我不能确定我的方法代表我 getName
对象可以 确实 被铸造 Func<string>
, , 不知何故?
我意识到有一个 MethodInfo.Invoke
, ,我也意识到我总是可以 创造 一个 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);
(我意识到那是不存在的;我想知道是否有什么 喜欢 它。)
解决方案
这种替代了我以前的答案,因为这是一条稍长的路线 - 给您一个快速的方法调用,与其他一些答案不同,您可以通过不同的实例(如果您遇到多个实例,相同类型)。如果您不想要,请检查我的底部更新(或查看Ben M的答案)。
这是一种执行您想要的测试方法:
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);
}
一旦您建立了一次代表 - 您可以在字典中缓存它:
Dictionary<Type, Func<object, string>> _methods;
然后,您要做的就是将其添加到字典中,使用传入对象的类型(从getType())作为键。将来,您首先检查一下词典中是否有现成的代表(如果是这样),否则首先构建它,添加它,然后调用它。
顺便说一句,这是DLR为其动态调度机制所做的类型的非常简化的版本(在C#术语中,就是您使用“动态”关键字时)。
最后
如少数人提到的那样,您只想烘烤一个直接绑定到您收到的对象的函数,然后执行此操作:
[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);
}
但是请注意,一旦表达树被扔掉,您需要确保 (由于本·M的评论而删除)o
留在范围内,否则您可能会获得一些令人讨厌的结果。最简单的方法是保留您代表一生的本地参考(也许在课堂实例中)。
其他提示
是的,这是可能的:
Func<string> func = (Func<string>)
Delegate.CreateDelegate(typeof(Func<string>), getName);
这是我的答案,通过构建表达树。与其他答案不同,结果(getNameFunc
)是与原始实例绑定的函数 - 无需将其作为参数传递。
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
您需要创建一个新的委托实例。此选项比 Expression
基于基于示例。但是,通过基于表达的示例,可以创建一个 Func<MyClass, string> getNameFuncForAny
曾经,您可以重复使用的每个实例 MyClass
.
要创建这样的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();
}
您可以构建代表lambda调用此方法的表达树,然后 Compile()
因此,进一步的呼叫将与标准编译的呼叫一样快。
或者,我写了 这个方法 一段时间以前,基于一篇很棒的MSDN文章,该文章生成了一个使用IL的包装器来调用任何 MethodInfo
速度比 MethodInfo.DynamicInvoke
由于一旦生成代码,因此在普通呼叫上几乎没有开销。
我的头部方法的一个是使用动态。然后您可以这样这样:
if( /* This method can be a Func<string> */)
{
dynamic methodCall = myObject;
string response = methodCall.GetName();
}