C# .NET:从动态程序集访问非公共成员

C# .NET:从动态程序集访问非公共成员,c#,.net,performance,expression-trees,dynamic-assemblies,C#,.net,Performance,Expression Trees,Dynamic Assemblies,我正在开发一个允许用户输入任意表达式的库。然后,我的库将这些表达式作为较大表达式的一部分编译为委托。现在,由于未知的原因,使用Compile编译表达式有时/通常会导致代码的速度远远低于未编译表达式时的速度。我之前说过,一种解决方法是不使用编译,而是使用编译方法,并在新的动态程序集中的新类型上创建一个静态方法。这很有效,代码也很快 但用户可以输入任意表达式,结果表明,如果用户调用非公共函数或访问表达式中的非公共字段,则在调用委托时会抛出System.MethodAccessException(对于

我正在开发一个允许用户输入任意表达式的库。然后,我的库将这些表达式作为较大表达式的一部分编译为委托。现在,由于未知的原因,使用
Compile
编译表达式有时/通常会导致代码的速度远远低于未编译表达式时的速度。我之前说过,一种解决方法是不使用
编译
,而是使用
编译方法
,并在新的动态程序集中的新类型上创建一个
静态
方法。这很有效,代码也很快

但用户可以输入任意表达式,结果表明,如果用户调用非公共函数或访问表达式中的非公共字段,则在调用委托时会抛出
System.MethodAccessException
(对于非公共方法)

这里我可能要做的是创建一个新的
ExpressionVisitor
,检查表达式是否访问任何非公共内容,并在这些情况下使用较慢的
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;
    }
  }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.Linq.Expressions;
运用系统反思;
使用System.Reflection.Emit;
命名空间动态合理
{
公共课程
{
私有静态int GetValue()
{
返回1;
}
public static int GetValuePublic()
{
返回1;
}
公共静态int-Foo;
静态void Main(字符串[]参数)
{
表达式=()=>10+GetValue();
Foo=expression.Compile();
Console.WriteLine(“这个有效,值:+Foo”);
表达式expressionPublic=()=>10+GetValuePublic();
var compiledDynamicAssemblyPublic=(Func)CompileExpression(expressionPublic);
Foo=compiledDynamicAssemblyPublic();
WriteLine(“这也行,值:+Foo”);
var compiledDynamicAssemblyNonPublic=(Func)CompileExpression(表达式);
Console.WriteLine(“此崩溃”);
Foo=compiledDynamicAssemblyNonPublic();
}
静态委托编译表达式(LambdaExpression表达式)
{
var assemblyBuilder=AppDomain.CurrentDomain.DefinedDynamicAssembly(
新的AssemblyName(“MyAssembly”+Guid.NewGuid().ToString(“N”),
AssemblyBuilderAccess.Run);
var moduleBuilder=assemblyBuilder.DefinedDynamicModule(“模块”);
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”);
返回函数;
}
}
}

我曾经在使用DynamicMethod从生成的IL代码访问类的私有元素时遇到问题

原来类
DynamicMethod
的构造函数有一个重载,它将类的类型接收到允许私有访问的位置:

此链接包含如何访问私有数据的示例。。。我知道这与表达式树无关,但它可能会为您提供一些有关如何执行的线索


在编译表达式树时可能会有类似的事情。。。或者,您可以将该表达式树作为动态方法创建。

问题不在于权限,因为没有权限允许您访问非公共字段或其他类的成员而不进行反射。这类似于编译两个非动态程序集,一个程序集在第二个程序集中调用公共方法的情况。然后,如果在不重新编译第一个程序集的情况下将方法更改为private,则第一个程序集调用将在运行时失败。换句话说,动态程序集中的表达式被编译成一个普通的方法调用,与您从另一个类(即使在同一程序集中)调用该方法相比,它没有任何权限调用该方法

因为没有权限可以解决您的问题,所以您可以将非公共字段和方法引用转换为使用反射的子表达式

下面是一个来自您的测试用例的示例。这失败了:

Expression<Func<int>> expression = () => 10 + GetValue();
Expression=()=>10+GetValue();
但这将成功:

Expression<Func<int>> expression = () => 10 + (int)typeof(Program).GetMethod("GetValue", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, null);
Expression Expression=()=>10+(int)typeof(Program).GetMethod(“GetValue”,BindingFlags.Static | BindingFlags.NonPublic).Invoke(null,null);

由于这不会因异常而崩溃,您可以看到您的动态程序集确实具有反射权限,并且它可以访问私有方法,因此它无法使用
CompileToMethod
生成的普通方法调用来执行此操作。

如果非动态程序集是由您生成的,实际上,您可以为动态程序集包含一个
InternalsVisibleTo
(甚至可以使用强名称)。这将允许使用内部成员,这在您的情况下可能就足够了

为了了解这一点,下面的示例展示了如何使Moq的动态程序集能够使用来自另一个程序集的内部内容:


如果这种方法还不够,我会结合Rick和Miguel的建议:为对非公共成员的每次调用创建“代理”动态方法,并更改表达式树,以便使用它们而不是原始调用。

我没有答案,但是为什么有必要支持调用私有方法呢?因为用户希望这应该是可能的。因为它们是可访问的