Jul*_*anR 18 .net c# performance expression-trees dynamic-assemblies
我正在开发一个允许用户输入任意表达式的库.然后,我的库将这些表达式作为更大表达式的一部分编译到委托中.现在,由于仍然未知的原因,Compile
有时/经常编译表达式导致代码远远低于它不是编译表达式时的代码.之前我问了一个关于这个的问题,一个解决方法是不使用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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
问题不在于权限,因为没有权限可以允许您在没有反射的情况下访问非公共字段或其他类的成员.这类似于编译两个非动态程序集和一个程序集在第二个程序集中调用公共方法的情况.然后,如果将方法更改为private而不重新编译第一个程序集,则第一个程序集调用现在将在运行时失败.换句话说,动态程序集中的表达式被编译为一个普通的方法调用,即使在同一个程序集中,它也没有其他类调用的权限.
由于没有权限可以解决您的问题,您可以将非公共字段和方法引用转换为使用反射的子表达式.
以下是从您的测试用例中获取的示例.这失败了:
Expression<Func<int>> expression = () => 10 + GetValue();
Run Code Online (Sandbox Code Playgroud)
但这会成功:
Expression<Func<int>> expression = () => 10 + (int)typeof(Program).GetMethod("GetValue", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, null);
Run Code Online (Sandbox Code Playgroud)
由于这不会因异常而崩溃,您可以看到动态程序集确实具有反射权限,并且它可以访问私有方法,它只是无法使用导致的普通方法调用来执行此操作CompileToMethod
.