给出以下代码行,
!apt-get update && apt-get install caffe-cpu -y --allow-unauthenticated
是否有一种 fast 方法来获取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
)以及@IvanStoev的代码(DelegateDynamicInvoke
,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
答案 0 :(得分:4)
唯一可以避免耗时的Compile
操作的方法是使用反射来递归地评估表达式内容。
一般地执行此操作(处理所有情况)是一项复杂的任务。当前有80多个ExpressionType,它们都具有不同的语义(当然,有些属于具有相应基类的类别)。为了处理所有这些,可能应该创建自定义ExpressionVisitor并实现评估引擎(可能带有某种评估堆栈)。
换句话说,很多工作/代码。
但是...如果将表达式限制为两种类型-ConstantExpression(常量值)和MemberExpression(常量值的字段或属性),那么有一个相对简单的解决方案。所讨论的方法已经包含关于传递的Expression<Action>
的假设,并且样本表达式目标(为闭包)属于恒定值字段类别。
主要工作是通过私有递归方法完成的,如下所示:
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);
}
我没有性能比较的结果,但是即使它使用反射,它也应该比使用反射和动态IL代码发出的Compile
快得多,这还不算创建DynamicMethod
并委托它进行调用。
答案 1 :(得分:1)
随着时间的推移,我对 Ivan 的解决方案进行了扩展,以防它对其他人有所帮助
static object Evaluate(
Expression expression
)
{
switch (expression)
{
case ConstantExpression e:
return e.Value;
case MemberExpression e when e.Member is FieldInfo field:
return field.GetValue(
Evaluate(
e.Expression
)
);
case MemberExpression e when e.Member is PropertyInfo property:
return property.GetValue(
Evaluate(
e.Expression
)
);
case ListInitExpression e when e.NewExpression.Arguments.Count() == 0:
var collection = e.NewExpression.Constructor.Invoke(new object[0]);
foreach(var i in e.Initializers)
{
i.AddMethod.Invoke(
collection,
i.Arguments
.Select(
a => Evaluate(a)
)
.ToArray()
);
}
return collection;
case MethodCallExpression e:
return e.Method.Invoke(
Evaluate(e.Object),
e.Arguments
.Select(
a => Evaluate(a)
)
.ToArray()
);
default:
//TODO: better messaging
throw new NotSupportedException();
}
}