gpt4 book ai didi

c# - 快速获取 Expression 方法调用目标的方法

转载 作者:太空宇宙 更新时间:2023-11-03 20:52:17 24 4
gpt4 key购买 nike

给定以下代码行,

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(); }

但是非常非常慢。


有没有更快的方法来获取方法调用表达式的目标?


对我的代码(GetDelegateDelegateCompileDelegateDynamicInvoke)以及@IvanStoev 的代码(GetFunc)进行基准测试, FuncCompileFuncInvoke) 产生这个结果:

|                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操作是使用反射递归地评估表达式内容。

一般地执行此操作(处理所有情况)是一项复杂的任务。目前有80多个ExpressionType s,它们都具有不同的语义(好吧,有些属于具有相应基类的类别)。为了处理所有这些,可能应该创建自定义 ExpressionVisitor并实现评估引擎(可能使用某种评估堆栈)。

换句话说,大量的工作/代码。

但是...如果我们将表达式限制为 2 种类型 - ConstantExpression (常数值)和 MemberExpression (常量值的字段或属性),则有一个相对容易的解决方案。有问题的方法已经包含关于传递的假设 Expression<Action>并且示例表达式目标(它是一个闭包)属于常量值字段类别。

主要工作在私有(private)递归方法中完成如下:

static object Evaluate(Expression expression)
{
if (expression == null)
return null;
if (expression is ConstantExpression constExpression)
return constExpression.Value;
if (expression is MemberExpression memberExpression)
{
var target = Evaluate(memberExpression.Expression);
if (memberExpression.Member is FieldInfo field)
return field.GetValue(target);
if (memberExpression.Member is PropertyInfo property)
return property.GetValue(target);
}
throw new NotSupportedException();
}

使用它的方法是

public object GetExpressionTarget<T>(Expression<T> expression)
{
var methodCall = (MethodCallExpression)expression.Body;
return Evaluate(methodCall.Object);
}

我没有性能比较结果,但即使这是使用反射,它应该比 Compile 快得多它使用反射 动态 IL 代码发出,不计算 DynamicMethod 的创建并委托(delegate)调用它。

关于c# - 快速获取 Expression 方法调用目标的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54112050/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com