visual studio programar online ideone ejemplos ejecutar compiladores codigo c# compiler-construction

studio - programar c# online



¿Es posible compilar dinámicamente y ejecutar fragmentos de código C#? (6)

Me preguntaba si es posible guardar fragmentos de código C # en un archivo de texto (o cualquier flujo de entrada), y luego ejecutarlos dinámicamente. Asumiendo que lo que se me proporciona compilaría bien dentro de cualquier bloque Main (), ¿es posible compilar y / o ejecutar este código? Preferiría compilarlo por motivos de rendimiento.

Por lo menos, podría definir una interfaz que se les requeriría implementar, luego proporcionarían una ''sección'' de código que implementara esa interfaz.


El espacio de nombres System.CodeDom.Compiler debería ayudar. Ver este artículo


La mejor solución en C # / todos los lenguajes .NET estáticos es usar el CodeDOM para tales cosas. (Como nota, su otro propósito principal es construir dinámicamente bits de código, o incluso clases completas).

Aquí hay un buen ejemplo breve tomado del blog de LukeH , que también utiliza algunos LINQ solo por diversión.

using System; using System.Collections.Generic; using System.Linq; using Microsoft.CSharp; using System.CodeDom.Compiler; class Program { static void Main(string[] args) { var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } }); var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true); parameters.GenerateExecutable = true; CompilerResults results = csc.CompileAssemblyFromSource(parameters, @"using System.Linq; class Program { public static void Main(string[] args) { var q = from i in Enumerable.Range(1,100) where i % 2 == 0 select i; } }"); results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText)); } }

La clase de importancia principal aquí es CSharpCodeProvider que utiliza el compilador para compilar el código sobre la marcha. Si desea ejecutar el código, solo necesita usar un poco de reflexión para cargar dinámicamente el ensamblado y ejecutarlo.

Here hay otro ejemplo en C # que (aunque un poco menos conciso) además le muestra precisamente cómo ejecutar el código compilado en tiempo de ejecución utilizando el System.Reflection nombres System.Reflection .


Lo encontré útil: asegura que el ensamblado compilado haga referencia a todo lo que ha referenciado actualmente, ya que hay muchas posibilidades de que desee el C # que está compilando para usar algunas clases, etc., en el código que lo emite:

var refs = AppDomain.CurrentDomain.GetAssemblies(); var refFiles = refs.Where(a => !a.IsDynamic).Select(a => a.Location).ToArray(); var cSharp = (new Microsoft.CSharp.CSharpCodeProvider()).CreateCompiler(); var compileParams = new System.CodeDom.Compiler.CompilerParameters(refFiles); compileParams.GenerateInMemory = true; compileParams.GenerateExecutable = false; var compilerResult = cSharp.CompileAssemblyFromSource(compileParams, code); var asm = compilerResult.CompiledAssembly;

En mi caso, estaba emitiendo una clase, cuyo nombre estaba almacenado en una cadena, className , que tenía un solo método público estático llamado Get() , que regresaba con el tipo StoryDataIds . Así es como se llama a ese método:

var tempType = asm.GetType(className); var ids = (StoryDataIds)tempType.GetMethod("Get").Invoke(null, null);


Otros ya han dado buenas respuestas sobre cómo generar código en tiempo de ejecución, así que pensé que abordaría su segundo párrafo. Tengo algo de experiencia con esto y solo quiero compartir una lección que aprendí de esa experiencia.

Por lo menos, podría definir una interfaz que se les requeriría implementar, luego proporcionarían una ''sección'' de código que implementara esa interfaz.

Puede tener un problema si usa una interface como tipo base. Si agrega un único método nuevo a la interface en el futuro, todas las clases existentes suministradas por el cliente que implementan la interface se vuelven abstractas, lo que significa que no podrá compilar o instanciar la clase suministrada por el cliente en el tiempo de ejecución.

Tuve este problema cuando llegó el momento de agregar un nuevo método después de aproximadamente 1 año de enviar la interfaz anterior y después de distribuir una gran cantidad de datos "heredados" que debían ser compatibles. Terminé creando una nueva interfaz heredada de la anterior, pero este enfoque hizo más difícil cargar y crear instancias de las clases suministradas por el cliente porque tenía que verificar qué interfaz estaba disponible.

Una solución en la que pensé en ese momento fue utilizar una clase real como tipo base, como la que se muestra a continuación. La clase en sí misma se puede marcar como abstracta, pero todos los métodos deben ser métodos virtuales vacíos (no métodos abstractos). Los clientes pueden anular los métodos que desean y puedo agregar nuevos métodos a la clase base sin invalidar el código existente proporcionado por el cliente.

public abstract class BaseClass { public virtual void Foo1() { } public virtual bool Foo2() { return false; } ... }

Independientemente de si este problema se aplica, debería considerar cómo versionar la interfaz entre su código base y el código proporcionado por el cliente.


Para compilar, puede iniciar una llamada de shell al compilador de csc. Es posible que tenga un dolor de cabeza tratando de mantener sus caminos y cambios correctos, pero sin duda se puede hacer.

Ejemplos de C # Corner Shell

EDITAR : O mejor aún, use el CodeDOM como sugirió Noldorin ...


Puede compilar una pieza C # de código en la memoria y generar bytes de ensamblaje con Roslyn. Ya se mencionó, pero valdría la pena agregar algún ejemplo de Roslyn para esto aquí. El siguiente es el ejemplo completo:

using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Emit; namespace RoslynCompileSample { class Program { static void Main(string[] args) { SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(@" using System; namespace RoslynCompileSample { public class Writer { public void Write(string message) { Console.WriteLine(message); } } }"); string assemblyName = Path.GetRandomFileName(); MetadataReference[] references = new MetadataReference[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location) }; CSharpCompilation compilation = CSharpCompilation.Create( assemblyName, syntaxTrees: new[] { syntaxTree }, references: references, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); using (var ms = new MemoryStream()) { EmitResult result = compilation.Emit(ms); if (!result.Success) { IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error); foreach (Diagnostic diagnostic in failures) { Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage()); } } else { ms.Seek(0, SeekOrigin.Begin); Assembly assembly = Assembly.Load(ms.ToArray()); Type type = assembly.GetType("RoslynCompileSample.Writer"); object obj = Activator.CreateInstance(type); type.InvokeMember("Write", BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, new object[] { "Hello World" }); } } Console.ReadLine(); } } }