首页 / 软件开发 / C# / 与动态执行的C#代码进行通讯
与动态执行的C#代码进行通讯2011-07-27 博客园 xiaotie1、简介能够动态执行 C# 代码是一件很酷的功能,比如,我们可以在控制台中输入一行 C# 代码,然后程序 自动编译并执行这一行代码,将结果显示给我们。这差不多就是一个最简单的 C# 代码解释器了。动态执行 C# 代码又是一件很有用的功能,比如,我们可以将某些代码写在某个文件之中,由程序集 在执行时进行加载,改变这些代码不用中止程序,当程序再次加载这些代码时,就自动执行的是新代码了 。下面,我将在写一个简单C# 代码解释器,然后将在 C# 代码解释器之中加入动态代码与解释器环境间 的动态交互机制,来演示一个很好很强大的应用。2、简单的 C# 代码解释器关于如何动态执行 C# 代码在 Jailu.Net 的《如何用C#动态编译、执行代码》一文中讲述的很清晰。 采用该文所述方式写一个 C# 代码解释器:using System;
using System.Collections.Generic;
using System.Reflection;
using System.Globalization;
using Microsoft.CSharp;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Text;
using System.IO;
using System.Xml;
namespace Test
{
class Program
{
static void Main(string[] args)
{
Console.Write(">> ");
String cmd;
Context cxt = new Context();
while ((cmd = Console.ReadLine().Trim()) != "exit")
{
if (!String.IsNullOrEmpty(cmd))
{
Console.WriteLine();
cxt.Invoke(cmd);
}
Console.Write("
>> ");
}
}
}
public class Context
{
public CSharpCodeProvider CodeProvider { get; set; }
public IDictionary<String, Assembly> Assemblys { get; set; }
public Context()
{
CodeProvider = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });
Assemblys = new Dictionary<String, Assembly>();
Assembly[] al = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly a in al)
{
AddAssembly(a);
}
AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler (CurrentDomain_AssemblyLoad);
}
private void AddAssembly(Assembly a)
{
if (a != null)
{
Assemblys.Add(a.FullName, a);
}
}
void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
Assembly a = args.LoadedAssembly;
if (!Assemblys.ContainsKey(a.FullName))
{
AddAssembly(a);
}
}
public CompilerParameters CreateCompilerParameters()
{
CompilerParameters cp = new CompilerParameters();
cp.GenerateExecutable = false;
cp.GenerateInMemory = true;
if (Assemblys != null)
{
foreach (Assembly a in Assemblys.Values)
{
cp.ReferencedAssemblies.Add(a.Location);
}
}
return cp;
}
public void Invoke(String cmd)
{
String inputCmdString = cmd.Trim();
if (String.IsNullOrEmpty(inputCmdString)) return;
String fullCmd = BuildFullCmd(inputCmdString);
CompilerResults cr = CodeProvider.CompileAssemblyFromSource (CreateCompilerParameters(), fullCmd);
if (cr.Errors.HasErrors)
{
Boolean recompileSwitch = true;
foreach (CompilerError err in cr.Errors)
{
//CS0201 : Only assignment, call, increment, decrement, and new object expressions can be
//used as a statement
if (!err.ErrorNumber.Equals("CS0201"))
{
recompileSwitch = false;
break;
}
}
// 重新编译
if (recompileSwitch)
{
String dynaName = "TempArg_Dynamic_" + DateTime.Now.Ticks.ToString ();
inputCmdString = String.Format(" var {0} = ", dynaName) + inputCmdString;
inputCmdString += ";
System.Console.WriteLine(" + dynaName + ");";
fullCmd = BuildFullCmd(inputCmdString);
cr = CodeProvider.CompileAssemblyFromSource(CreateCompilerParameters (), fullCmd);
}
if (cr.Errors.HasErrors)
{
Console.WriteLine("编译错误:");
foreach (CompilerError err in cr.Errors)
{
Console.WriteLine(err.ErrorNumber);
Console.WriteLine(err.ErrorText);
}
return;
}
}
Assembly assem = cr.CompiledAssembly;
Object dynamicObject = assem.CreateInstance("Test.DynamicClass");
Type t = assem.GetType("Test.DynamicClass");
MethodInfo minfo = t.GetMethod("MethodInstance");
minfo.Invoke(dynamicObject, null);
}
private String BuildFullCmd(String inputCmdString)
{
String fullCmd = String.Empty;
fullCmd += @"
namespace Test
{
public class DynamicClass
{
public void MethodInstance()
{
" + inputCmdString + @";
}
}
}";
return fullCmd;
}
}
}