Algunas ocasiones podriamos requerir generar un interprete de scripts o compilar y ejecutar código de forma dinámica, como para el procesamiento de plantillas, una forma de hacer esto es como lo muestra el siguiente listado de código.
Ejemplo de utilización
StringBuilder code = new StringBuilder();
code.AppendLine ("namespace Sample { ");
code.AppendLine ("public class Evaluator{");
code.AppendLine ("public static double EvalExpression(){");
code.AppendLine ("return 3*3;");
code.AppendLine ("}");
code.AppendLine ("}");
code.AppendLine ("}");
F.Runtime.Compiler compiler = compiler = new F.Runtime.Compiler(F.Runtime.Language.CSharp, code.ToString ());
try
{
compiler.CompilerErrors = new System.CodeDom.Compiler.CompilerErrorCollection();
MessageBox.Show(
compiler.Execute("Sample", "Evaluator", "EvalExpression", null, true).ToString()
);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Listado de código
using Microsoft.VisualBasic; using Microsoft.CSharp; using System; using System.Text; using System.CodeDom.Compiler; using System.Reflection; using System.IO; using System.Diagnostics; using System.Collections; namespace F.Runtime{ #region Enumerates public class Constants { public static char vbCrLf { get { return '\n'; } } } public enum Language { VB, CSharp } #endregion /// <summary> /// Runtime Dot Net Compiler And Executor /// </summary> /// <remarks></remarks> /// #region Compiler Class public class Compiler { #region Private Members private Language m_lang; private ArrayList m_dlls; private ArrayList m_namespaces; private CompilerErrorCollection m_oCompilerErrors; private string m_cod; private object RunningInstance; private MethodInfo TheMethod; private string m_userDefFunctions = string.Empty; #endregion #region Properties /// <summary> /// Target dotNet Language to compile /// </summary> /// <value></value> /// <returns></returns> /// <remarks></remarks> public Language Language { get { return m_lang; } set { m_lang = value; } } /// <summary> /// Dlls to be referenced /// </summary> /// <value></value> /// <returns></returns> /// <remarks></remarks> public ArrayList Dlls { get { if (m_dlls == null) { m_dlls = new ArrayList(); } return m_dlls; } set { m_dlls = value; } } /// <summary> /// Namespaces to be included /// </summary> /// <value></value> /// <returns></returns> /// <remarks></remarks> public ArrayList Namespaces { get { if (m_namespaces == null) { m_namespaces = new ArrayList(); } return m_namespaces; } set { m_namespaces = value; } } /// <summary> /// Dot Net Code to compile /// </summary> /// <value></value> /// <returns></returns> /// <remarks></remarks> public string Code { get { return m_cod; } set { m_cod = value; } } /// <summary> /// Compiler Error Collection /// </summary> /// <value></value> /// <returns></returns> /// <remarks></remarks> public CompilerErrorCollection CompilerErrors { get { if (m_oCompilerErrors!=null) m_oCompilerErrors = new System.CodeDom.Compiler.CompilerErrorCollection(); return m_oCompilerErrors; } set { m_oCompilerErrors = value; try { Int16 i; for (i = 0; i <= m_oCompilerErrors.Count - 1; i++) { string[] lines = m_cod.Split(Constants.vbCrLf); if (lines.Length >= m_oCompilerErrors[i].Line) { m_oCompilerErrors[i].ErrorText += lines[m_oCompilerErrors[i].Line - 1]; // ERROR: Unknown assignment operator ConcatString ; } } } catch { } } } public string UserDefinedFunctions { get { return m_userDefFunctions; } set { m_userDefFunctions = value; } } #endregion #region Contructors /// <summary> /// Default class constructor /// </summary> /// <remarks></remarks> public Compiler() : base() { m_oCompilerErrors = new CompilerErrorCollection(); m_lang = Language.VB; RunningInstance = null; TheMethod = null; } /// <summary> /// Overloads class constructor taking the dotNet language and code as parameters /// </summary> /// <param name="Lang"></param> /// <param name="theCode"></param> /// <remarks></remarks> public Compiler(Language Lang, string theCode)// ERROR: Unsupported modifier : In, Optional string theCode) : this() { m_lang = Lang; m_cod = theCode; } #endregion #region Public Methods /// <summary> /// /// </summary> /// <param name="counter"></param> /// <param name="paramDef"></param> /// <param name="body"></param> /// <returns></returns> /// <remarks></remarks> public string EvalDef(Int16 counter, string paramDef, string body) { string toRet = string.Empty; switch (m_lang) { case Language.CSharp: toRet = Constants.vbCrLf + "public object Eval" + counter + " (" + paramDef + ") {" + Constants.vbCrLf + body + Constants.vbCrLf + "}" + Constants.vbCrLf; break; case Language.VB: toRet = Constants.vbCrLf + "Public Function Eval" + counter + " (" + paramDef + ") As Object " + Constants.vbCrLf + body + Constants.vbCrLf + "End Function" + Constants.vbCrLf; break; } return toRet; } /// <summary> /// Public method to evaluate the code /// </summary> /// <param name="paramDeclarition">Parameter declaration</param> /// <param name="pCode">Function Body Code</param> /// <param name="params">Array of parameters</param> /// <param name="recompile">Flag to do a recompilation</param> /// <returns></returns> /// <remarks></remarks> public object Eval(string paramDeclarition, string pCode, object[] @params, bool recompile) // ERROR: Unsupported modifier : In, Optional bool recompile) { object toRet = null; switch (m_lang) { case Language.CSharp: toRet = EvalCS(paramDeclarition, pCode, @params, recompile); break; case Language.VB: toRet = EvalVB(paramDeclarition, pCode, @params, recompile); break; } return toRet; } /// <summary> /// Private method to evaluate the VB code /// </summary> /// <param name="paramDeclarition">Parameter declaration</param> /// <param name="vbCode">Function Body VB Code</param> /// <param name="params">Array of parameters</param> /// <param name="recompile">Flag to do a recompilation</param> /// <returns></returns> /// <remarks></remarks> private object EvalVB(string paramDeclarition, string vbCode, object[] @params, bool recompile) // ERROR: Unsupported modifier : In, Optional bool recompile) { object toRet = null; Type IntanceType; if (RunningInstance == null | recompile) { VBCodeProvider oCodeProvider = new VBCodeProvider(); CompilerParameters compilerParameters = new CompilerParameters(); CompilerResults compilerResults; System.Reflection.Assembly AssemblyInstance; try { compilerParameters.ReferencedAssemblies.Add("system.dll"); compilerParameters.ReferencedAssemblies.Add("system.xml.dll"); compilerParameters.ReferencedAssemblies.Add("system.data.dll"); if ((m_dlls != null)) { foreach (string d in m_dlls) { compilerParameters.ReferencedAssemblies.Add(d); } } compilerParameters.CompilerOptions = "/t:library"; compilerParameters.GenerateInMemory = true; StringBuilder sb = new StringBuilder(string.Empty); sb.Append("Imports System" + Constants.vbCrLf); sb.Append("Imports Microsoft.VisualBasic" + Constants.vbCrLf); sb.Append("Imports System.Text" + Constants.vbCrLf); sb.Append("Imports System.Collections" + Constants.vbCrLf); sb.Append("Imports System.Collections.Generic" + Constants.vbCrLf); sb.Append("Imports System.Text.RegularExpressions" + Constants.vbCrLf); sb.Append("Imports System.Xml" + Constants.vbCrLf); sb.Append("Imports System.Data" + Constants.vbCrLf); if ((m_namespaces != null)) { foreach (string n in m_namespaces) { sb.Append("Imports " + n + Constants.vbCrLf); } } sb.Append("Namespace CompilerEngine" + Constants.vbCrLf); sb.Append("Public Class RunTimeArtifact " + Constants.vbCrLf); sb.Append(UserDefinedFunctions); sb.Append("Public Function Eval(" + paramDeclarition + ") As Object " + Constants.vbCrLf); sb.Append(vbCode + Constants.vbCrLf); sb.Append("End Function " + Constants.vbCrLf); sb.Append("End Class " + Constants.vbCrLf); sb.Append("End Namespace" + Constants.vbCrLf); m_cod = sb.ToString(); this.m_oCompilerErrors.Clear(); try { compilerResults = oCodeProvider.CompileAssemblyFromSource(compilerParameters, m_cod); // Check for compile time errors if (compilerResults.Errors.Count != 0) { this.CompilerErrors = compilerResults.Errors; throw new Exception("Compile Errors"); } else { // No Errors On Compile, so continue to process... AssemblyInstance = compilerResults.CompiledAssembly; RunningInstance = AssemblyInstance.CreateInstance("CompilerEngine.RunTimeArtifact"); IntanceType = RunningInstance.GetType(); TheMethod = IntanceType.GetMethod("Eval"); toRet = TheMethod.Invoke(RunningInstance, @params); } } catch { throw; } } catch { throw; } } else { if (TheMethod == null) { IntanceType = RunningInstance.GetType(); TheMethod = IntanceType.GetMethod("Eval"); } toRet = TheMethod.Invoke(RunningInstance, @params); } return toRet; } /// <summary> /// Private method to evaluate the CSharp code /// </summary> /// <param name="paramDeclarition">Parameter declaration</param> /// <param name="csCode">Function Body CSharp Code</param> /// <param name="params">Array of parameters</param> /// <param name="recompile">Flag to do a recompilation</param> /// <returns></returns> /// <remarks></remarks> private object EvalCS(string paramDeclarition, string csCode, object[] @params, bool recompile) // ERROR: Unsupported modifier : In, Optional bool recompile) { object toRet = null; Type IntanceType; if (RunningInstance == null | recompile) { CSharpCodeProvider oCodeProvider = new CSharpCodeProvider(); CompilerParameters compilerParameters = new CompilerParameters(); CompilerResults compilerResults; System.Reflection.Assembly AssemblyInstance; try { compilerParameters.ReferencedAssemblies.Add("system.dll"); compilerParameters.ReferencedAssemblies.Add("system.xml.dll"); compilerParameters.ReferencedAssemblies.Add("system.data.dll"); if ((m_dlls != null)) { foreach (string d in m_dlls) { compilerParameters.ReferencedAssemblies.Add(d); } } compilerParameters.CompilerOptions = "/t:library"; compilerParameters.GenerateInMemory = true; StringBuilder sb = new StringBuilder(string.Empty); sb.Append("using System;" + Constants.vbCrLf); sb.Append("using Microsoft.CSharp;" + Constants.vbCrLf); sb.Append("using System.Text;" + Constants.vbCrLf); sb.Append("using System.Collections;" + Constants.vbCrLf); sb.Append("using System.Collections.Generic;" + Constants.vbCrLf); sb.Append("using System.Text.RegularExpressions;" + Constants.vbCrLf); sb.Append("using System.Xml;" + Constants.vbCrLf); sb.Append("using System.Data;" + Constants.vbCrLf); if ((m_namespaces != null)) { foreach (string n in m_namespaces) { sb.Append("using " + n + ";" + Constants.vbCrLf); } } // Build a little wrapper code, with our passed in code in the middle sb.Append("namespace CompilerEngine{" + Constants.vbCrLf); sb.Append(UserDefinedFunctions); sb.Append("public class RunTimeArtifact {" + Constants.vbCrLf); sb.Append("public object Eval(" + paramDeclarition + "){" + Constants.vbCrLf); sb.Append(csCode + Constants.vbCrLf); sb.Append("}" + Constants.vbCrLf); sb.Append("}" + Constants.vbCrLf); sb.Append("}" + Constants.vbCrLf); m_cod = sb.ToString(); this.m_oCompilerErrors.Clear(); try { compilerResults = oCodeProvider.CompileAssemblyFromSource(compilerParameters, m_cod); // Check for compile time errors if (compilerResults.Errors.Count != 0) { this.CompilerErrors = compilerResults.Errors; throw new Exception("Compile Errors"); } else { // No Errors On Compile, so continue to process... AssemblyInstance = compilerResults.CompiledAssembly; RunningInstance = AssemblyInstance.CreateInstance("CompilerEngine.RunTimeArtifact"); IntanceType = RunningInstance.GetType(); TheMethod = IntanceType.GetMethod("Eval"); toRet = TheMethod.Invoke(RunningInstance, @params); } } catch { throw; } } catch { throw; } } else { if (TheMethod == null) { IntanceType = RunningInstance.GetType(); TheMethod = IntanceType.GetMethod("Eval"); } toRet = TheMethod.Invoke(RunningInstance, @params); } return toRet; } /// <summary> /// Compiles and Execute a given code and method /// </summary> /// <param name="Namespace">Enclosing namespace</param> /// <param name="ClassName">Class name</param> /// <param name="Method">Method to Execute</param> /// <param name="params">Parameter array</param> /// <param name="recompile">Flag to force a recompilation</param> /// <returns></returns> /// <remarks></remarks> public object Execute(string Namespace, string ClassName, string Method, object[] @params, bool recompile) // ERROR: Unsupported modifier : In, Optional bool recompile) { object toRet = null; Type IntanceType; if (RunningInstance == null | recompile) { object oCodeProvider = null; switch (m_lang) { case Language.CSharp: oCodeProvider = new CSharpCodeProvider(); break; case Language.VB: oCodeProvider = new VBCodeProvider(); break; } CompilerParameters compilerParameters = new CompilerParameters(); CompilerResults compilerResults = null; System.Reflection.Assembly AssemblyInstance; try { compilerParameters.ReferencedAssemblies.Add("system.dll"); compilerParameters.ReferencedAssemblies.Add("system.xml.dll"); compilerParameters.ReferencedAssemblies.Add("system.data.dll"); if ((m_dlls != null)) { foreach (string d in m_dlls) { if (d.Trim().Length > 0) { compilerParameters.ReferencedAssemblies.Add(d); } } } compilerParameters.CompilerOptions = "/t:library"; compilerParameters.GenerateInMemory = true; this.m_oCompilerErrors.Clear(); try { switch (m_lang) { case Language.CSharp: compilerResults = ((CSharpCodeProvider)oCodeProvider).CompileAssemblyFromSource(compilerParameters, m_cod); break; case Language.VB: compilerResults = ((VBCodeProvider)oCodeProvider).CompileAssemblyFromSource(compilerParameters, m_cod); break; } // Check for compile time errors if (compilerResults.Errors.Count != 0) { this.CompilerErrors = compilerResults.Errors; throw new Exception("Compile Errors"); } else { // No Errors On Compile, so continue to process... AssemblyInstance = compilerResults.CompiledAssembly; RunningInstance = AssemblyInstance.CreateInstance(Namespace + "." + ClassName); IntanceType = RunningInstance.GetType(); TheMethod = IntanceType.GetMethod(Method); toRet = TheMethod.Invoke(RunningInstance, @params); } } catch { throw; } } catch { throw; } } else { bool getMethod = false; if (TheMethod == null) { getMethod = true; } else if (!TheMethod.Name.ToLower().Equals(Method.ToLower())) { getMethod = true; } if (getMethod) { IntanceType = RunningInstance.GetType(); TheMethod = IntanceType.GetMethod(Method); } toRet = TheMethod.Invoke(RunningInstance, @params); } return toRet; } /// <summary> /// Code Setter function /// </summary> /// <param name="_dlls">quoted separed list of assemblies, (by E., system.dll,system.xml.dll,system.data.dll,... )</param> /// <param name="_imports">quoted separed list of namespaces to import (by E. System.Data,System.Text,System.XML,System.Collections,...)</param> /// <param name="_code">source code body namespace and inner classes</param> /// <returns></returns> /// <remarks></remarks> public string SetCode(string _dlls, string _imports, string _code) { string[] dl; string[] im; m_cod = string.Empty; dl = _dlls.Split(','); im = _imports.Split(','); Dlls.Clear(); foreach (string s in dl) { if (s.Trim().Length > 0) { Dlls.Add(s); } } switch (m_lang) { case Language.CSharp: foreach (string s in im) { if (s.Trim().Length > 0) { m_cod += "using " + s + ";\n";// ERROR: Unknown assignment operator ConcatString ; } } break; case Language.VB: foreach (string s in im) { if (s.Trim().Length > 0) { m_cod += "Imports " + s + "\n";// ERROR: Unknown assignment operator ConcatString ; } } break; } m_cod += _code;// ERROR: Unknown assignment operator ConcatString ; return m_cod; } #endregion } #endregion }