Dan*_*Tao 57 .net reflection performance func methodinfo
我意识到,一般来说,使用反射会产生性能影响.(事实上,我自己并不是反思的粉丝;这是一个纯粹的学术问题.)
假设存在一些看起来像这样的类:
public class MyClass {
public string GetName() {
return "My Name";
}
}
Run Code Online (Sandbox Code Playgroud)
跟我来这儿.我知道如果我有一个MyClass被叫的实例x,我可以打电话x.GetName().此外,我可以设置一个Func<string>变量x.GetName.
现在这是我的问题.假设我不知道上面的类叫做MyClass; 我有一些对象,x但我不知道它是什么.我可以GetName通过这样做来检查该对象是否有方法:
MethodInfo getName = x.GetType().GetMethod("GetName");
Run Code Online (Sandbox Code Playgroud)
假设getName不为空.然后,我不能进一步检查getName.ReturnType == typeof(string)和getName.GetParameters().Length == 0,在这一点上,我岂不是很肯定的是,在我所代表的方法,getName对象可以肯定被转换为一个Func<string>,不知何故?
我意识到有一个MethodInfo.Invoke,我也意识到我总能创造一个Func<string>像:
Func<string> getNameFunc = () => getName.Invoke(x, null);
Run Code Online (Sandbox Code Playgroud)
我想我要问的是,是否有任何方法可以从一个MethodInfo对象转到它所代表的实际方法,从而在该过程中产生反射的性能成本,但在此之后能够直接调用该方法(例如,通过a 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);
Run Code Online (Sandbox Code Playgroud)
(我意识到这不存在;我想知道是否有类似的东西.)
And*_*tan 38
这种方式取代了我以前的答案,因为这虽然是一个稍微长一点的路径 - 给你一个快速的方法调用,并且与其他一些答案不同,它允许你通过不同的实例(如果你要遇到多个实例)相同类型).如果您不想这样,请查看底部的更新(或查看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);
}
Run Code Online (Sandbox Code Playgroud)
一旦构建委托 - 您可以将其缓存在字典中:
Dictionary<Type, Func<object, string>> _methods;
Run Code Online (Sandbox Code Playgroud)
然后,您只需使用传入对象的Type(来自GetType())作为键,将其添加到字典中.在将来,您首先检查字典中是否有一个现成的委托(如果是这样,则调用它),否则先构建它,添加它,然后调用它.
顺便说一句,这是DLR为其动态调度机制所做的事情的非常高度简化的版本(在C#术语中,当您使用'dynamic'关键字时).
最后
如果像少数人提到的那样,你只是想直接将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);
}
Run Code Online (Sandbox Code Playgroud)
但请注意,一旦表达式树被抛弃,您需要确保(由于Ben M的评论而删除)o保留在范围内,否则您可能会得到一些令人讨厌的结果.最简单的方法是在委托的生命周期内保持本地引用(可能在类实例中).
Jul*_*anR 16
是的,这是可能的:
Func<string> func = (Func<string>)
Delegate.CreateDelegate(typeof(Func<string>), getName);
Run Code Online (Sandbox Code Playgroud)
Ben*_*n M 14
这是我的答案,通过构建表达式树.与其他答案不同,result(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();
}
}
Run Code Online (Sandbox Code Playgroud)
最简单的方法是通过Delegate.CreateDelegate:
Func<string> getNameFunc = (Func<string>) Delegate.CreateDelegate(
typeof(Func<string>), x, getName);
Run Code Online (Sandbox Code Playgroud)
请注意,这会绑定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();
}
Run Code Online (Sandbox Code Playgroud)
您可以这样使用:
Func<MyClass, string> getNameFuncForAny = GetInstanceMethod(getName);
MyClass x1 = new MyClass();
MyClass x2 = new MyClass();
string result1 = getNameFuncForAny(x1);
string result2 = getNameFuncForAny(x2);
Run Code Online (Sandbox Code Playgroud)
如果您不想被束缚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();
}
Run Code Online (Sandbox Code Playgroud)