.NET中的动态定制手段(Part 1)C#

类别:编程语言 点击:0 评论:0 推荐:

总想抽点时间把前一段时间的研究成果写下来,现在终于可以了,.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
}

}
全加在一块,不过二十几行程序,还是比较实用的,至于效率,只要设计得合理,只有在载入时才会编译脚本,其余时候与正常程序并无二样,而且如果设计得好的话,可以动态支持多种脚本,加上第三方提供的编译功能,真是太方便了。建议使用在特别容易改变规则的地方,缺点就是底层代码直接就可以被别人看到,对脚本的修改不适当(尤其是别有用心的修改)的话,程序很容易崩溃。所以建议只用来定制限制和校验。简单多变的服务就不要用了吧。至于的动态定制,下回再说。

本文地址:http://com.8s8s.com/it/it23174.htm