总想抽点时间把前一段时间的研究成果写下来,现在终于可以了,.NET为我们提供了很多新的、好玩的功能,如果不用岂不是糟蹋了。 我对动态定制的定义是:在编写程序时(甚至在编译程序时)都不用定义自己所要用的东西,都在运行的时候来决定,甚至是在运行的时候‘动态的’将其生成。 脚本定制是其中之一。 下面介绍一种.NET下比较好用的脚本定制方法,它充分利用了.NET FrameWork提供的动态编译功能,代码量也非常小。效率也不低。比如(我不会举例子,瞎举一下,不要太在意)我实现了一个装载MyClass的容器MyContainer,我考虑到需要对添加MyClass的方法MyContainer.Add加以限制,比如有MyClass A了,就不可以 向MyContainer中添加MyClass B了。如果我硬编码,那么如果限制改变了(比如A、B可以共存了,而A、C不能共存了),那么我的代码就必须进行改变(其实就是改几行代码再重新编译一遍,也没什么麻烦的),但是首先程序是不能再运行了,必须重启。但是如果你是使用脚本来进行校验的,你可以让你的程序监控着脚本文件,如果脚本发生改变,就重新编译脚本,对程序进行动态改变,那么你的程序就不必重启,也不必重新编译(但是你必须作出抉择,容器中的内容未必是‘安全’的了,因为限制改了,过去是正确的现在则未必了,容器至少要清空)。 那么对这个问题怎么进行脚本定制呢? 我的方案如下:(借鉴自Netron Graphlib) 首先,定义一个接口IScript,
public interface IScript { void Intialise(MyContainer m); bool Check(MyObject o); } 接着是在MyContainer.Add中加几行代码 public int Add(MyObject o) { CodeDomProvider provider = null; provider = new Microsoft.CSharp.CSharpCodeProvider();//.NET起码提供了C#和VB.NET的编译器, //可能还有第三方提供的,我不知道。 //这一部分可以用FactoryMethod //我只是演示就直接用C#了 ICodeCompiler compiler = provider.CreateCompiler(); //得到编译器 CompilerParameters parms = new CompilerParameters(); CompilerResults results = null; //配置编译参数 parms.MainClass="Script"; parms.GenerateExecutable = false; parms.GenerateInMemory = true; parms.TempFiles=new TempFileCollection(Path.GetTempPath(),false); parms.IncludeDebugInformation = false; parms.ReferencedAssemblies.Add("System.dll"); //添加引用 foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) { parms.ReferencedAssemblies.Add(asm.Location); } TextReader reader = File.OpenText(“CheckScript.cs“);//当然,可以读取配置,也可以监控,我就不写了 string Source = reader.ReadToEnd(); results = compiler.CompileAssemblyFromSource(parms, Source); Assembly ass = results.CompiledAssembly; foreach(Type type in ass.GetTypes()) { if(type is IScripter) { IScript Checker = ass.CreateInstance(type.FullName); Checker.Intialise(this); if(!Checker.Check(o)) return; //累死我了,就不写得更漂亮些了 } } //代码部分 }
脚本示例如下(CheckScript.cs)
using System; using System.Diagnostics; using MyNameSpace; namespace MyNameSpace.Scripts { public class Script: IScript , System.IDisposable { private static int counter = 0; //计数器 MyContainer m; public void Initialize( MyContainer m) { this.m= m; }
public bool Check(MyObject o) { //随你喜欢的代码,一定要返回啊!不然会出异常的! } #region "IDisposable implementation" public virtual void Dispose() { Dispose( true ); System.GC.SuppressFinalize( this); }
public virtual void Dispose( bool disposing ) { if ( ( disposing ) ) {
// Free other state (managed objects). } // Free your own state (unmanaged objects). // Set large fields to null. }
~Script() { // Simply call Dispose(false). Dispose( false ); } #endregion }
} 全加在一块,不过二十几行程序,还是比较实用的,至于效率,只要设计得合理,只有在载入时才会编译脚本,其余时候与正常程序并无二样,而且如果设计得好的话,可以动态支持多种脚本,加上第三方提供的编译功能,真是太方便了。建议使用在特别容易改变规则的地方,缺点就是底层代码直接就可以被别人看到,对脚本的修改不适当(尤其是别有用心的修改)的话,程序很容易崩溃。所以建议只用来定制限制和校验。简单多变的服务就不要用了吧。至于的动态定制,下回再说。

|