¿Se puede usar Roslyn para generar un método dynamic similar a la generación DynamicMethod IL?

He estado usando DynamiMethod para generar el IL usando

method.GetILGenerator(); 

Esto funciona bien pero, por supuesto, es muy difícil de usar, ya que generalmente no desea trabajar con IL de bajo nivel en un lenguaje de alto nivel como C #. Ahora, ya que está Roslyn, puedo usar eso en su lugar. He tratado de averiguar cómo usar Roslyn para hacer algo similar: generar un método dynamic y luego crear un delegado para él. La única forma en que pude hacerlo es tener una clase completa como esta

 SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(@" using System; namespace RoslynCompileSample { public class Writer { public void Write(string message) { Console.WriteLine(message); } } }"); 

Luego, en lugar del método de escritura, puedo insertar mi método dentro de la concatenación de cadenas. Después de que ese ensamblaje dynamic se genere en la memoria y se cargue y se use la reflexión para obtener el método requerido y generar el delegado.

Este método parece funcionar bien, pero parece un poco excesivo para mi caso, ya que tendré que usar múltiples métodos independientes posibles, lo que llevará a que se carguen muchos ensamblajes.

Entonces, la pregunta es: ¿hay una manera fácil de hacer algo similar al método dynamic para Roslyn, de modo que solo pueda definir un cuerpo del método asociado a un tipo? Si no es así, ¿hay algún gran inconveniente al comstackr muchos ensamblajes dynamics (como demasiados no se pueden cargar, etc …)

Puedes usar la clase CSharpScript . await CSharpScript.EvaluateAsync("1 + 2") solo evalúa la expresión. Puede encontrarlo en Microsoft.CodeAnalysis.Scripting.CSharp paquete Microsoft.CodeAnalysis.Scripting.CSharp (actualmente solo la versión preliminar). Agregue referencias de uso y ensamblaje utilizando ScriptOptions (segundo parámetro).

Comstackr la expresión para delegar:

 var func = CSharpScript.Create("1 + 3").CompileToDelegate() 

Pasando algo a la función mediante objeto global:

 await CSharpScript.Create("1 + x", ScriptOptions.Default.AddReferences(typeof(Program).Assembly), globalsType: typeof(ScriptGlobals)) .CreateDelegate() .Invoke(new ScriptGlobals() { x = 4 }); 

Tengo una idea más de cómo resolver su problema, que no utiliza Roslyn en absoluto. Usted describió que es molesto emitir IL usando ILGenerator . Sin embargo, .NET Framework tiene árboles semánticos incorporados, que pueden comstackrse a métodos dynamics. Viven en el espacio de nombres de Linq.Expression y también se utilizan en los proveedores de Linq.

 var parameter = Expression.Parameter(typeof(int), "a"); // define parameter var body = Expression.Add(parameter, Expression.Constant(42)); // sum parameter and number var lambdaExpression = Expression.Lambda>(new[] { parameter }, body); // define method var add42Delegate = lambdaExpression.Compile(); // compile to dynamic method 

Puede hacer casi cualquier cosa con su uso, es mucho más cómodo que ILGenerator y está incluido en la biblioteca estándar.

Me gustaría comentar sobre la respuesta de exyi con Expression y Func , pero no tengo suficiente reputación. Así que aquí viene mi “respuesta” en su lugar.

Si todo lo que necesita es un código de ciudadano de primera clase que puede ejecutar con parámetros, simplemente puede crear el Lambda así:

 Func add42 = number => number + 42; // Called like this: int theNumber46 = add42.Invoke(4); 

Si necesita tener el árbol de expresiones real, también hay un atajo limpio:

 Expression> add42 = number => number + 42; // Called like this: int theNumber46 = add42.Compile().Invoke(4); 

La única diferencia en el código es que envolvió la Func con una Expression<..> . La diferencia conceptual es que un Lambda (o Func<> en este ejemplo, pero también hay otros Lambdas) puede ejecutarse tal como está, mientras que una Expression<> debe comstackrse primero con el método Compile() . Pero Expression contiene información sobre el árbol de syntax y, por lo tanto, puede usarse para proveedores de datos IQueriable como se usa en EntityFramework.

Entonces, todo depende de lo que quieras hacer con tu método dynamic / lamda / delegate.