Estoy trabajando en una biblioteca que permite a los usuarios ingresar expresiones arbitrarias. Mi biblioteca luego compila esas expresiones como parte de una expresión más grande en un delegado. Ahora, por razones todavía desconocidas, compilar la expresión con Compile
a veces/a menudo da como resultado un código que es mucho más lento de lo que sería si no fuera una expresión compilada. I asked a question about this antes y una solución fue no usar Compile
, pero CompileToMethod
y crear un método static
en un nuevo tipo en un nuevo conjunto dinámico. Eso funciona y el código es rápido..NET: Acceder a miembros no públicos desde un ensamblaje dinámico
Pero los usuarios pueden ingresar expresiones arbitrarias y resulta que si el usuario llama a una función no pública o accede a un campo no público en la expresión, arroja un System.MethodAccessException
(en el caso de un método no público) cuando el delegado es invocado
Lo que probablemente podría hacer aquí es crear un nuevo ExpressionVisitor
que compruebe si la expresión tiene acceso a algo no público y usar el Compile
más lento en esos casos, pero prefiero que el ensamblaje dinámico de alguna manera tenga los derechos de acceso los miembros no públicos. O descubro si hay algo que pueda hacer para que Compile
sea más lento (algunas veces).
El código completo para reproducir este problema:
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;
}
}
}
No tengo una respuesta para usted, pero ¿por qué es necesario apoyar métodos privados de llamadas? – jlew
Porque el usuario espera que sea posible. Porque * son * accesibles cuando crea la expresión, como '() => CallPrivateMethod()', pero fallarán en el tiempo de ejecución. No hay nada para él que indique que no funciona hasta que lo ejecute y se cuelgue y arda. Eso es realmente malo y viola la regla de "menos sorpresa", así que no puedo justificarlo y tendré que conformarme con el código lento. – JulianR
Tiene sentido, si el usuario es un programador de C# (a diferencia de alguien escribiendo expresiones en un formulario, por ejemplo). ¿Ha evaluado el modo versión versus depuración para el delegado compilado? ¿Cómo se comparan? – jlew