【发布时间】:2011-04-22 19:00:32
【问题描述】:
我正在开发一个允许用户输入任意表达式的库。然后,我的库将这些表达式作为更大表达式的一部分编译到委托中。现在,由于仍然未知的原因,使用Compile 编译表达式有时/经常导致代码比它不是编译表达式时要慢得多。我之前asked a question about this,一种解决方法是不使用Compile,而是使用CompileToMethod,并在新动态程序集中的新类型上创建static 方法。这行得通,而且代码很快。
但是用户可以输入任意表达式,结果如果用户调用非公共函数或访问表达式中的非公共字段,则会抛出System.MethodAccessException(在非公共方法的情况下) ) 当委托被调用时。
我在这里可能做的是创建一个新的ExpressionVisitor,它检查表达式是否访问任何非公开的内容,并在这些情况下使用较慢的Compile,但我宁愿让动态程序集以某种方式获得访问非公共成员的权利。或者找出我可以对Compile 变慢(有时)做些什么。
重现此问题的完整代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
namespace DynamicAssembly
{
public class Program
{
private static int GetValue()
{
return 1;
}
public static int GetValuePublic()
{
return 1;
}
public static int Foo;
static void Main(string[] args)
{
Expression<Func<int>> expression = () => 10 + GetValue();
Foo = expression.Compile()();
Console.WriteLine("This works, value: " + Foo);
Expression<Func<int>> expressionPublic = () => 10 + GetValuePublic();
var compiledDynamicAssemblyPublic = (Func<int>)CompileExpression(expressionPublic);
Foo = compiledDynamicAssemblyPublic();
Console.WriteLine("This works too, value: " + Foo);
var compiledDynamicAssemblyNonPublic = (Func<int>)CompileExpression(expression);
Console.WriteLine("This crashes");
Foo = compiledDynamicAssemblyNonPublic();
}
static Delegate CompileExpression(LambdaExpression expression)
{
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName("MyAssembly"+ Guid.NewGuid().ToString("N")),
AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("Module");
var typeBuilder = moduleBuilder.DefineType("MyType", TypeAttributes.Public);
var methodBuilder = typeBuilder.DefineMethod("MyMethod",
MethodAttributes.Public | MethodAttributes.Static);
expression.CompileToMethod(methodBuilder);
var resultingType = typeBuilder.CreateType();
var function = Delegate.CreateDelegate(expression.Type,
resultingType.GetMethod("MyMethod"));
return function;
}
}
}
【问题讨论】:
-
我没有给你答案,但是为什么要支持调用私有方法呢?
-
因为用户期望它应该是可能的。因为当他创建表达式时,它们是可访问的,例如
() => CallPrivateMethod(),但它们会在运行时失败。对他来说,没有任何迹象表明它在他运行它并且它崩溃和燃烧之前不起作用。这真的很糟糕,并且违反了“最少意外”的规则,所以我不能证明这样做是合理的,我将不得不接受缓慢的代码。 -
有意义,如果用户是 C# 程序员(而不是有人在表单中输入表达式,例如)。您是否对已编译委托的发布模式和调试模式进行了基准测试?它们如何相互比较?
-
@jlew - 是的,它将成为程序员使用的库。至于编译表达式树的性能细节,我可以参考我的链接问题,其中有深入的:)
-
这可能是一个半生不熟的想法,也许您可以通过将表达式树中对私有方法的调用替换为对作为参数传递给 MethodBuilder 方法的委托的调用来分割性能差异.然后,将私有方法包装在委托中,并将它们作为参数传递给您的新方法。这将解决 MethodAccess 问题,但谁知道它是否会否定性能优势。
标签: c# .net performance expression-trees dynamic-assemblies