与动态执行的C# 代码进行通讯
1、简介
能够动态执行 C# 代码是一件很酷的功能,比如,我们可以在控制台中输入一行 C# 代码,然后程序自动编译并执行这一行代码,将结果显示给我们。这差不多就是一个最简单的 C# 代码解释器了。
动态执行 C# 代码又是一件很有用的功能,比如,我们可以将某些代码写在某个文件之中,由程序集在执行时进行加载,改变这些代码不用中止程序,当程序再次加载这些代码时,就自动执行的是新代码了。
下面,我将在写一个简单C# 代码解释器,然后将在 C# 代码解释器之中加入动态代码与解释器环境间的动态交互机制,来演示一个很好很强大的应用。
2、简单的 C# 代码解释器
关于如何动态执行 C# 代码在 Jailu.Net 的《如何用C#动态编译、执行代码》一文中讲述的很清晰。采用该文所述方式写一个 C# 代码解释器:
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("\n>> ");
}
}
}
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 += ";\n 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;
}
}
}
编译执行后就得到一个傻傻的 C# 代码解析器,也可以当一个简单的计算器用:
3、解释器与所解释的代码之间进行变量交互
如果将所解释的代码中的某些变量储存下来,供给以后的代码用,这一解释器的功能又会强大很多。假设这类变量名称以$打头,如:
$myblogname = “http://xiaotie.cnblogs.com”
将在解释器环境中定义(如果该变量未存在)或赋值于(如果该变量已存在)一个名为 myblogname 的字符串变量,指向字符串“http://xiaotie.cnblogs.com”。而,System.Console.WriteLine($myblogname)则取出并打印出字符串该变量所引用的。
简单说来,也就是让所解释的代码中能够初始化并引用解释器中的变量。
如何实现呢?这是本文的重点。
首先,在 Context 类中定义一个SortedDictionary储存变量,并提供索引访问:
public Object this[String instanceName]
{
get
{
if (Instances.ContainsKey(instanceName))
{
return Instances[instanceName];
}
else
{
return null;
}
}
set
{
if (Instances.ContainsKey(instanceName))
{
Instances.Remove(instanceName);
}
Instances.Add(instanceName, value);
}
}
BuildFullCmd方法改变为:
{
String fullCmd = String.Empty;
fullCmd += @"
using Test;
public class DynamicClass
{
private Context m_context;
public void MethodInstance(Context context)
{
m_context = context;
" + inputCmdString + @";
}
}";
return fullCmd;
}
这样,在动态生成的对象中,便可以引用Context对象。
对于inputCmdString 中未定义的外部变量,在第一次遇见时将$argname替换为一个随机生成的内部变量,在代码的最后,将这个内部变量储存在 Context 中。
虽然通过 (Context[argname].GetType())(Context[argname]) 便可引用外部变量 $argname,但是这样引用赋值时,编译器会报错。解决这个问题需要一个新的类:
{
private String m_objName;
public Context Context { get; private set; }
public T Obj
{
get
{
Object obj = Context[m_objName];
return (T)obj;
}
set { Context[m_objName] = value; }
}
public ObjectHelper(Context cxt, String objName)
{
m_objName = objName;
Context = cxt;
}
}
将inputCmdString中的外部变量$argname统一替换为(new ObjectHelper <m_context[“argname”].GetType()> (m_context, “argname”)).Obj" 即可实现在动态代码中对已定义外部变量的引用。
上述对inputCmdString的预处理代码为:
// 处理未初始化的环境变量
re = new Regex(@"^(\$)(\w)+");
if (inputCmdString != null)
{
Match m = re.Match(inputCmdString);
if (m != null && m.Length > 1)
{
String outArgName = inputCmdString.Substring(m.Index, m.Length).Substring(1);
if (this[outArgName] == null)
{
String innerArgName = "TempArg_" + outArgName;
inputCmdString = "var " + inputCmdString.Replace("$" + outArgName, innerArgName);
inputCmdString += ";m_context[\"" + outArgName + "\"]=" + innerArgName + ";";
}
}
}
// 处理其它环境变量
re = new Regex(@"(\$)(\w)+");
IDictionary<String, String> ArgsList = new Dictionary<String, String>();
if (inputCmdString != null)
{
MatchCollection mc = re.Matches(inputCmdString);
if (mc != null)
{
foreach (Match m in mc)
{
if (m.Length > 1)
{
String outArgName = inputCmdString.Substring(m.Index, m.Length).Substring(1);
if (!ArgsList.ContainsKey(outArgName))
{
Object obj = this[outArgName];
if (obj == null) throw new Exception("不存在环境变量" + outArgName);
String innerArgName = String.Format(@"(new ObjectHelper<{0}>(m_context,""{1}"")).Obj", obj.GetType(), outArgName);
ArgsList.Add(outArgName, innerArgName);
}
}
}
}
foreach (String outArg in ArgsList.Keys)
{
inputCmdString = inputCmdString.Replace("$" + outArg, ArgsList[outArg]);
}
}
这里做了个简化,即定义外部变量的格式必须为 $argname = value,其中 $argname 必须在行首。
这样,对于:$myblogname = "http://xiaotie.cnblogs.com". 因为 myblogname 变量不存在,被解析为:
var TempArg_myblogname = "http://xiaotie.cnblogs.com";
m_context["myblogname"]=TempArg_myblogname;;
定义后,当再出现 $myblogname,则被解析为 (new ObjectHelper<System.String>(m_context,"myblogname")).Obj;
看看实际执行情况:
完整代码于此下载。
4、一个很好很强大的应用—---打入.Net 程序内部,看看其执行情况。
采用上面的方法改进了 OrcShell(OrcShell详情见我前面的随笔: 实现简单的CSharpShell -- OrcShell )。新版 OrcShell 程序于此下载(需要.Net 3.5)。基本上是一个可用的 小型 .Net Framework Shell 了,可以动态的查看、创建、执行 .Net 的类型了。不过,自动提示与完成功能还没有做,使用起来还是较不方便的。
help 指令可以查看常用指令列表:
lsc 列出当前命名空间中的类型和下属命名空间。格式: lsc [name]
dirc 同 lsc
cdc 改变当前的命名空间,格式: cdc [.|..|name]
my 查看全部变量。格式:my。可通过$ArgName来引用变量。
alias 查看全部别名。格式:alias
use 添加命名空间。格式: use [namespace]
unuse 移除命名空间。格式:unuse [namespace]
import 导入程序集,有两种导入方式: "import -f [fullpath]","import [partname]"
本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2008/03/01/1087448.html如需转载请自行联系原作者
xiaotie 集异璧实验室(GEBLAB)
与动态执行的C# 代码进行通讯相关推荐
- python动态执行代码_第6.6节 Python动态执行小结
一. Python动态执行支持通过输入数据流或文件传入Python源代码串,进行编译后执行,可以通过这种方式扩展Python程序的功能: 二. 动态执行方法可能导致恶意攻击,因此使用时需要 ...
- java动态执行逻辑_动态执行代码逻辑
动态执行逻辑的方法据我所知有一下两种方式 QLExpress Groovy QLExpress QLExpress是阿里开源的动态脚本执行的项目. 由阿里的电商业务规则.表达式(布尔组合).特殊数学公 ...
- python动态运行py代码_Python 动态执行
Python 动态执行 2015-06-16 Tuesday 在 Python 中,允许通过 exec 和 eval 执行以字符串形式表示的代码片段,这体现了动态语言的特性,可以让代码变得更灵活.实际 ...
- php执行查询的代码,PHP实现动态执行代码的方法
本文实例讲述了PHP实现动态执行代码的方法.分享给大家供大家参考,具体如下: 这里介绍的PHP动态执行,即在页面上直接输入代码,点击执行,返回执行结果 方法很简单,主要使用了: $newfunc = ...
- Java动态执行代码字符串
1. 步骤 假如我们需要动态执行一段字符串形式的java代码,大概需要这样几步: 生成文件(具体的文件/内存中的文件) 调用javac编译 通过反射执行 2. 生成文件 确定字符串的形式 字符串已经是 ...
- SQL Server 中 EXEC全称execute 与 SP_EXECUTESQL动态执行sql代码语句 的区别
先睹为快 use cplvfx_DBdeclare @sqlStr nvarchar(max)set @sqlStr='select top 100 * from User'exec (@sqlStr ...
- JQuery动态执行javascript代码的方法
在JQUERY中要动态执行从服务器端返回的HTML片断,可用JQUERY中的globalEval方法. 举例如下: var scriptCode='alert("hello")'; ...
- 动态执行流程分析和性能瓶颈分析的利器——gperftools的Cpu Profiler
在<动态执行流程分析和性能瓶颈分析的利器--valgrind的callgrind>中,我们领略了valgrind对流程和性能瓶颈分析的强大能力.本文将介绍拥有相似能力的gperftools ...
- 动态执行流程分析和性能瓶颈分析的利器——valgrind的callgrind
在<内存.性能问题分析的利器--valgrind>一文中我们简单介绍了下valgrind工具集,本文将使用callgrind工具进行动态执行流程分析和性能瓶颈分析.(转载请指明出于brea ...
最新文章
- angular 如何在index html 页面包含子页面_Webpack4.0各个击破(1)html篇
- windows 编程 —— 消息与参数(定时器、初始化消息、改变大小)
- the pdf expert for android v2.6.0,PDF Expert
- [js] js循环中调用异步的方法,如何确保执行结果的顺序是正确的?
- 算法(七):图解动态规划
- 二维向量vector初始化问题(以统计字符串字母题为例)
- 从JS库的使用者转变成JS的开发者——第一篇 javascript对象
- SAP PS:项目进度计划管理
- JAVA分布式(一)——了解分布式
- Ubuntu系统实现简单c语言编程
- cad转图片格式后不清晰怎么办?三步即可解决
- Python3学习笔记(1): print函数用法总结
- R语言笔记--par()函数详解
- (7)数据分析-秩和检验
- obsutil相关 (桶相关操作Linux)
- 了解黑客经常使用哪些工具
- 测设面试题笔试题总结
- Java软件如何加密
- ps切图--图层切图
- 自制肥鲨HDO2电源降压延长线,支持3S~6S动力电池
热门文章
- Windows Phone开发:常用控件(上)
- 【jquery仿datalist的一个问题,求助】——设置每行显示几列,块状DIV的解决办法...
- python数据分析神器_牛逼啊!一个随时随地写Python代码的神器
- Arduino可穿戴教程Linux平台下安装Arduino IDE
- Xamarin.iOS项目提示error MSB3174:”TargetFrameworkVersion”的值无效
- idea查看ruby代码_Ruby实现命令行中查看函数源码的方法
- java多线程系列_Java多线程实战系列
- java上传png_java图片上传
- fastreport.net 交叉表居中显示_浅析Sql中内连接、外连接、全连接、交叉连接的区别...
- linux下g编译文件或目录,【转】在linux下使用gcc/g++编译多个.h文件