给定以下代码行,
Expression<Action> expression = () => target.ToString();
有没有快速获取target
对象的方法?
下面的代码有效
public object GetExpressionTarget<T>(Expression<T> expression)
{
MethodCallExpression methodCall = (MethodCallExpression) expression.Body;
LambdaExpression theTarget = Expression.Lambda(methodCall.Object, null);
Delegate compiled = theTarget.Compile();
return compiled.DynamicInvoke(); }
但是非常非常慢。
有没有更快的方法来获取方法调用表达式的目标?
对我的代码 ( GetDelegate
,DelegateCompile
和DelegateDynamicInvoke
) 以及 @IvanStoev 的代码 ( GetFunc
,FuncCompile
和FuncInvoke
) 进行基准测试会产生以下结果:
| Method | Mean | Error | StdDev |
|---------------------- |----------------|---------------|---------------|
| DelegateCompile | 165,068.477 ns | 2,671.3001 ns | 2,498.7358 ns |
| FuncCompile | 160,956.199 ns | 2,133.5343 ns | 1,995.7093 ns |
| DelegateDynamicInvoke | 1,148.191 ns | 11.7213 ns | 10.9642 ns |
| FuncInvoke | 3.040 ns | 0.0264 ns | 0.0247 ns |
所以,Invoke
实际上比 快很多DynamicInvoke
,但瓶颈实际上是Compile
调用。有没有办法在target
不编译表达式的情况下获取对象?
基准代码:
public class Program
{
private Delegate @delegate;
private Func<object> func;
private static Delegate GetDelegate(Expression<Action> expression)
{
MethodCallExpression methodCall = (MethodCallExpression) expression.Body;
return Expression.Lambda(methodCall.Object, null).Compile();
}
private static Func<object> GetFunc(Expression<Action> expression)
{
MethodCallExpression methodCall = (MethodCallExpression) expression.Body;
return Expression.Lambda<Func<object>>(methodCall.Object).Compile();
}
[GlobalSetup]
public void Setup()
{
object o = new object();
Expression<Action> expression = () => o.ToString();
this.@delegate = Program.GetDelegate(expression);
this.func = Program.GetFunc(expression);
}
[Benchmark]
public void DelegateCompile()
{
object o = new object();
Expression<Action> expression = () => o.ToString();
Program.GetDelegate(expression);
}
[Benchmark]
public void FuncCompile()
{
object o = new object();
Expression<Action> expression = () => o.ToString();
Program.GetFunc(expression);
}
[Benchmark]
public void DelegateDynamicInvoke()
{
this.@delegate.DynamicInvoke();
}
[Benchmark]
public void FuncInvoke()
{
this.func.Invoke();
}
public static void Main(string[] args)
{
BenchmarkRunner.Run<Program>();
}
}