动态执行C 代码

动态执行C 代码1 简介能够动态执行 C 代码是一件很酷的功能 比如 我们可以在控制台中输入一行 C 代码 然后程序自动编译并执行这一行代码 将结果显示给我们

大家好,欢迎来到IT知识分享网。

1、简介

能够动态执行 C# 代码是一件很酷的功能,比如,我们可以在控制台中输入一行 C# 代码,然后程序自动编译并执行这一行代码,将结果显示给我们。这差不多就是一个最简单的 C# 代码解释器了。

动态执行 C# 代码又是一件很有用的功能,比如,我们可以将某些代码写在某个文件之中,由程序集在执行时进行加载,改变这些代码不用中止程序,当程序再次加载这些代码时,就自动执行的是新代码了。

下面,我将在写一个简单C# 代码解释器,然后将在 C# 代码解释器之中加入动态代码与解释器环境间的动态交互机制,来演示一个很好很强大的应用。

2、简单的 C# 代码解释器

关于如何动态执行 C# 代码在 Jailu.Net 的《如何用C#动态编译、执行代码》一文中讲述的很清晰。采用该文所述方式写一个 C# 代码解释器:

动态执行C 代码usingSystem;


动态执行C 代码usingSystem.Collections.Generic;


动态执行C 代码usingSystem.Reflection;


动态执行C 代码usingSystem.Globalization;


动态执行C 代码usingMicrosoft.CSharp;


动态执行C 代码usingSystem.CodeDom;


动态执行C 代码usingSystem.CodeDom.Compiler;


动态执行C 代码usingSystem.Text;


动态执行C 代码usingSystem.IO;


动态执行C 代码usingSystem.Xml;


动态执行C 代码


动态执行C 代码namespaceTest


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码classProgram


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码staticvoidMain(string[]args)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码Console.Write(“>>”);


动态执行C 代码Stringcmd;


动态执行C 代码Contextcxt=newContext();


动态执行C 代码while((cmd=Console.ReadLine().Trim())!=”exit”)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码if(!String.IsNullOrEmpty(cmd))


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码Console.WriteLine();


动态执行C 代码cxt.Invoke(cmd);


动态执行C 代码}


动态执行C 代码Console.Write(“/n>>”);


动态执行C 代码}


动态执行C 代码}


动态执行C 代码}


动态执行C 代码


动态执行C 代码publicclassContext


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码
动态执行C 代码publicCSharpCodeProviderCodeProvider
动态执行C 代码{get;set;}


动态执行C 代码
动态执行C 代码publicIDictionary<String,Assembly>Assemblys
动态执行C 代码{get;set;}


动态执行C 代码


动态执行C 代码publicContext()


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码
动态执行C 代码CodeProvider=newCSharpCodeProvider(newDictionary<string,string>()
动态执行C 代码{
动态执行C 代码{“CompilerVersion”,”v3.5″}});


动态执行C 代码Assemblys=newDictionary<String,Assembly>();


动态执行C 代码Assembly[]al=AppDomain.CurrentDomain.GetAssemblies();


动态执行C 代码foreach(Assemblyainal)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码AddAssembly(a);


动态执行C 代码}


动态执行C 代码AppDomain.CurrentDomain.AssemblyLoad+=newAssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);


动态执行C 代码}


动态执行C 代码


动态执行C 代码privatevoidAddAssembly(Assemblya)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码if(a!=null)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码Assemblys.Add(a.FullName,a);


动态执行C 代码}


动态执行C 代码}


动态执行C 代码


动态执行C 代码voidCurrentDomain_AssemblyLoad(objectsender,AssemblyLoadEventArgsargs)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码Assemblya=args.LoadedAssembly;


动态执行C 代码if(!Assemblys.ContainsKey(a.FullName))


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码AddAssembly(a);


动态执行C 代码}


动态执行C 代码}


动态执行C 代码


动态执行C 代码publicCompilerParametersCreateCompilerParameters()


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码CompilerParameterscp=newCompilerParameters();


动态执行C 代码cp.GenerateExecutable=false;


动态执行C 代码cp.GenerateInMemory=true;


动态执行C 代码if(Assemblys!=null)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码foreach(AssemblyainAssemblys.Values)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码cp.ReferencedAssemblies.Add(a.Location);


动态执行C 代码}


动态执行C 代码}


动态执行C 代码returncp;


动态执行C 代码}


动态执行C 代码


动态执行C 代码publicvoidInvoke(Stringcmd)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码StringinputCmdString=cmd.Trim();


动态执行C 代码if(String.IsNullOrEmpty(inputCmdString))return;


动态执行C 代码


动态执行C 代码StringfullCmd=BuildFullCmd(inputCmdString);


动态执行C 代码


动态执行C 代码CompilerResultscr=CodeProvider.CompileAssemblyFromSource(CreateCompilerParameters(),fullCmd);


动态执行C 代码


动态执行C 代码if(cr.Errors.HasErrors)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码BooleanrecompileSwitch=true;


动态执行C 代码


动态执行C 代码foreach(CompilerErrorerrincr.Errors)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码//CS0201:Onlyassignment,call,increment,decrement,andnewobjectexpressionscanbe


动态执行C 代码//usedasastatement


动态执行C 代码if(!err.ErrorNumber.Equals(“CS0201”))


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码recompileSwitch=false;


动态执行C 代码break;


动态执行C 代码}


动态执行C 代码}


动态执行C 代码


动态执行C 代码//重新编译


动态执行C 代码if(recompileSwitch)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码StringdynaName=”TempArg_Dynamic_”+DateTime.Now.Ticks.ToString();


动态执行C 代码inputCmdString=String.Format(“var{0}=”,dynaName)+inputCmdString;


动态执行C 代码inputCmdString+=”;/nSystem.Console.WriteLine(“+dynaName+”);”;


动态执行C 代码


动态执行C 代码fullCmd=BuildFullCmd(inputCmdString);


动态执行C 代码cr=CodeProvider.CompileAssemblyFromSource(CreateCompilerParameters(),fullCmd);


动态执行C 代码}


动态执行C 代码


动态执行C 代码if(cr.Errors.HasErrors)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码Console.WriteLine(“编译错误:”);


动态执行C 代码foreach(CompilerErrorerrincr.Errors)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码Console.WriteLine(err.ErrorNumber);


动态执行C 代码Console.WriteLine(err.ErrorText);


动态执行C 代码}


动态执行C 代码


动态执行C 代码return;


动态执行C 代码}


动态执行C 代码}


动态执行C 代码


动态执行C 代码Assemblyassem=cr.CompiledAssembly;


动态执行C 代码ObjectdynamicObject=assem.CreateInstance(“Test.DynamicClass”);


动态执行C 代码Typet=assem.GetType(“Test.DynamicClass”);


动态执行C 代码MethodInfominfo=t.GetMethod(“MethodInstance”);


动态执行C 代码minfo.Invoke(dynamicObject,null);


动态执行C 代码}


动态执行C 代码


动态执行C 代码privateStringBuildFullCmd(StringinputCmdString)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码StringfullCmd=String.Empty;


动态执行C 代码


动态执行C 代码fullCmd+=@”


动态执行C 代码namespaceTest


动态执行C 代码{


动态执行C 代码publicclassDynamicClass


动态执行C 代码{


动态执行C 代码publicvoidMethodInstance()


动态执行C 代码{


动态执行C 代码“+inputCmdString+@”;


动态执行C 代码}


动态执行C 代码}


动态执行C 代码}”;


动态执行C 代码returnfullCmd;


动态执行C 代码}


动态执行C 代码}


动态执行C 代码}


动态执行C 代码

编译执行后就得到一个傻傻的 C# 代码解析器,也可以当一个简单的计算器用:

动态执行C 代码

3、解释器与所解释的代码之间进行变量交互

如果将所解释的代码中的某些变量储存下来,供给以后的代码用,这一解释器的功能又会强大很多。假设这类变量名称以$打头,如:

$myblogname = “http://xiaotie.cnblogs.com”

将在解释器环境中定义(如果该变量未存在)或赋值于(如果该变量已存在)一个名为 myblogname 的字符串变量,指向字符串“http://xiaotie.cnblogs.com”。 而,System.Console.WriteLine($myblogname)则取出并打印出字符串该变量所引用的。

简单说来,也就是让所解释的代码中能够初始化并引用解释器中的变量。

如何实现呢?这是本文的重点。

首先,在 Context 类中定义一个SortedDictionary储存变量,并提供索引访问:

动态执行C 代码
动态执行C 代码publicSortedDictionary<String,Object>Instances
动态执行C 代码{get;set;}


动态执行C 代码publicObjectthis[StringinstanceName]


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码get


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码if(Instances.ContainsKey(instanceName))


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码returnInstances[instanceName];


动态执行C 代码}


动态执行C 代码else


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码returnnull;


动态执行C 代码}


动态执行C 代码}


动态执行C 代码set


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码if(Instances.ContainsKey(instanceName))


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码Instances.Remove(instanceName);


动态执行C 代码}


动态执行C 代码Instances.Add(instanceName,value);


动态执行C 代码}


动态执行C 代码}

BuildFullCmd方法改变为:

动态执行C 代码privateStringBuildFullCmd(StringinputCmdString)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码StringfullCmd=String.Empty;


动态执行C 代码


动态执行C 代码fullCmd+=@”


动态执行C 代码usingTest;


动态执行C 代码


动态执行C 代码publicclassDynamicClass


动态执行C 代码{


动态执行C 代码privateContextm_context;


动态执行C 代码


动态执行C 代码publicvoidMethodInstance(Contextcontext)


动态执行C 代码{


动态执行C 代码m_context=context;


动态执行C 代码“+inputCmdString+@”;


动态执行C 代码}


动态执行C 代码}”;


动态执行C 代码returnfullCmd;


动态执行C 代码}

这样,在动态生成的对象中,便可以引用Context对象。

对于inputCmdString 中未定义的外部变量,在第一次遇见时将$argname替换为一个随机生成的内部变量,在代码的最后,将这个内部变量储存在 Context 中。

虽然通过 (Context[argname].GetType())(Context[argname]) 便可引用外部变量 $argname,但是这样引用赋值时,编译器会报错。解决这个问题需要一个新的类:

动态执行C 代码publicclassObjectHelper<T>


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码privateStringm_objName;


动态执行C 代码


动态执行C 代码
动态执行C 代码publicContextContext
动态执行C 代码{get;privateset;}


动态执行C 代码


动态执行C 代码publicTObj


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码get


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码Objectobj=Context[m_objName];


动态执行C 代码return(T)obj;


动态执行C 代码}


动态执行C 代码
动态执行C 代码set
动态执行C 代码{Context[m_objName]=value;}


动态执行C 代码}


动态执行C 代码


动态执行C 代码publicObjectHelper(Contextcxt,StringobjName)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码m_objName=objName;


动态执行C 代码Context=cxt;


动态执行C 代码}


动态执行C 代码}

将inputCmdString中的外部变量$argname统一替换为(new ObjectHelper <m_context[“argname”].GetType()> (m_context, “argname”)).Obj” 即可实现在动态代码中对已定义外部变量的引用。

动态执行C 代码Regexre;


动态执行C 代码


动态执行C 代码//处理未初始化的环境变量


动态执行C 代码re=newRegex(@”^(/$)(/w)+”);


动态执行C 代码if(inputCmdString!=null)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码Matchm=re.Match(inputCmdString);


动态执行C 代码if(m!=null&&m.Length>1)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码StringoutArgName=inputCmdString.Substring(m.Index,m.Length).Substring(1);


动态执行C 代码if(this[outArgName]==null)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码StringinnerArgName=”TempArg_”+outArgName;


动态执行C 代码inputCmdString=”var”+inputCmdString.Replace(“$”+outArgName,innerArgName);


动态执行C 代码inputCmdString+=”;m_context[/””+outArgName+”/”]=”+innerArgName+”;”;


动态执行C 代码}


动态执行C 代码}


动态执行C 代码}


动态执行C 代码


动态执行C 代码//处理其它环境变量


动态执行C 代码re=newRegex(@”(/$)(/w)+”);


动态执行C 代码IDictionary<String,String>ArgsList=newDictionary<String,String>();


动态执行C 代码if(inputCmdString!=null)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码MatchCollectionmc=re.Matches(inputCmdString);


动态执行C 代码if(mc!=null)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码foreach(Matchminmc)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码if(m.Length>1)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码StringoutArgName=inputCmdString.Substring(m.Index,m.Length).Substring(1);


动态执行C 代码if(!ArgsList.ContainsKey(outArgName))


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码Objectobj=this[outArgName];


动态执行C 代码if(obj==null)thrownewException(“不存在环境变量”+outArgName);


动态执行C 代码StringinnerArgName=String.Format(@”(newObjectHelper<{0}>(m_context,””{1}””)).Obj”,obj.GetType(),outArgName);


动态执行C 代码ArgsList.Add(outArgName,innerArgName);


动态执行C 代码}


动态执行C 代码}


动态执行C 代码}


动态执行C 代码}


动态执行C 代码


动态执行C 代码foreach(StringoutArginArgsList.Keys)


动态执行C 代码
动态执行C 代码
动态执行C 代码{


动态执行C 代码inputCmdString=inputCmdString.Replace(“$”+outArg,ArgsList[outArg]);


动态执行C 代码}


动态执行C 代码}

这里做了个简化,即定义外部变量的格式必须为 $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;

看看实际执行情况:

动态执行C 代码

完整代码于此下载。

4、一个很好很强大的应用——打入.Net 程序内部,看看其执行情况。


动态执行C 代码

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]”

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/143588.html

(0)
上一篇 2025-05-02 16:20
下一篇 2025-05-02 16:26

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信