【发布时间】:2019-01-09 14:10:20
【问题描述】:
给定以下代码行,
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>();
}
}
【问题讨论】:
-
我认为
Compile是最慢的部分,但是Expression.Lambda<Func<object>>(methodCall.Object).Compile().Invoke()怎么样 -
@IvanStoev 谢谢你的想法,但遗憾的是这也不能提高性能。基准测试显示
187 us用于我的问题中的代码,195 us用于您提出的代码(这并没有更糟,但这不是我所希望的)。 -
您在测量时是否将
Compile()与Invoke()分开(如在相关代码中)?或者您正在测量整个方法?至少您可以看到DynamicInvoke和Invoke之间是否有区别。我仍然认为最慢的部分是Compile()方法,而不是调用(一种或另一种方式)。 -
@IvanStoev 请查看编辑。你说得对,
Compile实际上是最慢的部分。此外,您的方法比使用DynamicInvoke快得多,但由于它使用Compile,它仍然很慢。不过还是谢谢你的帮助 -
它应该说“获得赏金的答案会最佳地提出比问题中的代码工作(相当)快的代码”当然......
标签: c# performance delegates expression dynamic-invoke