zoukankan      html  css  js  c++  java
  • .NET 动态脚本语言Script.NET 开发指南

    前一篇文章介绍了《.NET 动态脚本语言Script.NET 入门指南 Quick Start》的基础知识,这一篇文章继续介绍Script.NET开发相关的内容。

    Script.NET IDE Environment

    以前提到的书写Script.NET脚本的方式,在Visual Studio中书写代码,然后以调试的方式运行代码。这种方式适合于对Script.NET不熟悉,或是发生了不可理解的错误后,才运用Visual Studio强大的调试功能查找问题。Script.NET也考虑到了开发脚本的环境,已经提供了一个IDE。

    image

    语法高亮显示,解析与运行脚本,输出结果到Output窗口中。这对于日常的脚本开发,已经够用。

    如果能给它配置上.NET Framework的智能提示,则会更加完美。

    Contract 契约

    Script.NET是动态的语言,从做Web开发中经常用到的JavaScript 一样,动态语言不需要在定义时指明变量的类型,而是在运行时,根据值来分配类型。

    先介绍一下Script.NET的异常处理,基本的结构和.NET一样

    try
    {
    ……
    }
    catch(e)
    {
    ……
    }
    finally
    {
    ……
    }

    再看动态语言的例子,比教下面两个例子,就帮助理解它的”动态”含义。

    image

    再来换一下参数,同样调用方法inc,代码是这样的

    function inc(a)
    {
      a=a+1;
      return a;
    }
    
    Console.WriteLine(inc('abc'));

    Output窗口中输出的结果是abc1。这个例子解释了动态语言的主要区别,在运行时判断类型。

    如果要求函数inc只能接受int类型的变量,则需要这样处理

    function inc(a)
    [
    pre(a is int);
    post();
    invariant();
    ]
    {
      a=a+1;
      return a;
    }
    
    Console.WriteLine(inc(10));

    如果再次用Console.WriteLine(inc('abc'));调用加了contract的inc函数,则会抛出script exception。

    这就是契约的作用,它的完整定义是这样的,在函数定义体body前加三个表达式

    [
     pre(expression);
     post(expression);
     invariant(expression);
    ]

    再来看contract中post的处理。来看下面的脚本

    function inc(a)
    [
    pre(a is int);
    post(a<5);
    invariant();
    ]
    {
      a=a+1;
      return a;
    }
    
    try
    {
    Console.WriteLine(inc(10));
    }
    catch(e)
    {
     Console.WriteLine(e);
    }
    finally
    {
    }

    F5运行代码,会看到Output窗口中输出异常Post condition for function call failed。

    如果用inc(3)调用,则不会抛出异常。从这个例子中,可以理解pre可以用来约束参数类型,post可以约束函数返回结果

    使用脚本创建.NET应用程序

    在前一节的入门中提到,Script可以引用Host的变量,并对它进行操作,Host也可以获取到Script中运行的结果。把场景换成是WinForms中的Form窗体,像下面的这个应用程序,则很容易实现。

    image

    把当前的窗体MainForm传入到Script中,Script脚本中可对传入的变量Form进行操作,设置Text属性。

    .NET Integration  .NET集成

    Script.NET可以使用.NET Framework现有的类型,以提供绝大部分场合需要的类型。

    sin = Math.Sin;
    Console.WriteLine(sin(0.75));
     
    Console.WriteLine(DateTime.Now);
     
    a='1,2,3,4';
    array=Regex.Split(a,',');
    foreach(a in array)
    Console.WriteLine(a);

    在使用.NET的正则表式Regex类型,都不需要using,直接拿来即用。

    像上面的Regex.Split是静态方法,如果是实例子方法,可使用using指令简化Script.NET脚本代码

    using (Math)
    {
     //Build-in Objects
     //using the Pow function from Math class
     a = Pow(2, 3);
     Console.WriteLine(a);
    }

    Script.NET内置了三个函数,

    eval – 评估表达式的值并返回(evaluates value of an expression) ,例子 a = eval('2+3*4');

    clear – 清空上下文变量(clears all variables in context),已经过时

    array - 创建数组(creates typed array of objects)

    如果需要扩展Script.NET的内置函数,可参考这个例子

    class Program
        {
            static void Main(string[] args)
            {                    
                RuntimeHost.Initialize();                 
                Script script = Script.Compile(@"
                            return Inc(10); ");
                script.Context.SetItem("Inc", new IncrementFunction());
    
                object result = script.Execute();
                Console.WriteLine(result);
                Console.ReadLine();
            }
        }
    
        public class IncrementFunction : IInvokable
        {      
            public bool CanInvoke()
            {
                return true;
            }
    
            public object Invoke(IScriptContext context, object[] args)
            {
                int obj =Convert.ToInt32(args[0]);
                return ++obj;
            }       
        }

    经过运算之后,result的结果是11。经过这样的处理,以后需要应用Inc函数的地方,都可直接调用。

    没有动态语言,而只用动态编译

    我想,我还是得举例说明一下,Script.NET动态语言的好处,用途,这样它的价值才能得到体现。

    我在做工作流的表达式语句判断时,有这样一段代码

    SalesOrder order = new SalesOrder("OE0913", 100);
    Rule dr = new Rule("this.OrderNo == \"OE0913\"",
                                       "Rule1",
                                       new Statement[] {                                        
                                       new Assignment("this.Failed", "true"),
                                       },
                                       new Statement[0]
                                      );
    Parser parser = new Parser();
    parser.Fields.Add("_orderNo");
    parser.Fields.Add("_failed");
    RuleSet drs = new RuleSet();
    drs.RuleTypes.Add(new RuleTypeSet(typeof(SalesOrder), new Rule[] { dr }));
    drs.Eval(parser);
    
    bool pass = order.Failed;
    drs.RunRules(order);
    pass = order.Failed;

    它要表达的含义就是条件语句

    IF OrderNo =’OE0913’

           THEN Failed=true;

    上面的.NET代码,是对这条规则语句的.NET封装。

    工作流引擎接受一个变量,代表当前的对象,比如这里的SalesOrder。我在代码中定义了一个IF ELSE 语句块,当OrderNo为OE0913时,它会运行语句this.Failed=true,否则跳过,之后,parser解析器加上两个环境变量_orderNo和_failed的值,并引用到上面的规则。第一次,取到的值是order.Failed,这个值是引擎直接传递过来的,接着的一句drs.RunRules(order),会传入对象SalesOrder(“OE0913”,100)并且运行规则,因符合规则this.OrderNo=’OE0913’,满足条件,运行语句this.Failed=true, 最后一句获取到的值pass=order.Failed,就是经过引擎运算之后的值。这几句代码,是我的工作流引擎中自定义规则编辑器的单元测试代码,可以帮助你理解。

    抱歉,如果你不能明白这个例子,请参考文章《信息化基础建设 工作流开发》,或者实现一个工作流的自定义规则编辑器,应该可以明白这几句代码的含义。试想一下,在没有动态语言的情况下,实现一个IF ELSE的条件,是相当困难的。

    代码片段 Code Snippet

    在熟悉了基础的Script.NET的语法后,你可能需要下面的代码片断作为参考,来实现自己的脚本逻辑。

    for 语句

    z = 0;
    for(i=0; i<10; i++)
    {
     z += 2;
    }

    foreach 语句 

    a=[1,2,3,4];
    s=0;
    foreach(c in a)
    {
     s+=c;
    }

    访问SQL Server数据库

    sql = DbProviderFactories.GetFactory("System.Data.SqlClient");
    connection = sql.CreateConnection();
    connection.ConnectionString = "Data Source=(local);Initial Catalog=Northwind;Integrated Security=True";
    connection.Open();
    
    command = sql.CreateCommand();
    command.Connection = connection;
    command.CommandText = "select * from Customers";
    
    reader = command.ExecuteReader();
    while (reader.Read())
    {
      Console.WriteLine(reader["CompanyName"]+". "+ reader["ContactName"]);
     }
    connection.Dispose();    

    操作Windows Forms窗体

    f =  new Form();
    f.Width = 320;
    f.Height = 240;
    f.Text =  'Script Demo';
    f.Show();
    
    g = Graphics.FromHwnd(f.Handle);
    for (i = 0; i<10; i++)
    g.DrawRectangle(Pens.Red,
                    new Rectangle(10+i*10 , 10+i*10, 
                                  290-i*20, 180-i*20 ) );
    System.Threading.Thread.Sleep(1500);

    访问网络,读取RSS聚合

    a = new XmlDocument();
    a.Load("http://protsyk.com/cms/?feed=rss2&cat=10");
    foreach (n in a.SelectNodes('/rss/channel/item'))
         Console.WriteLine(n['title'].InnerText + ' ' + n['link'].InnerText);

    泛型数组

    v = new int[10];
    for(i=0; i<v.Length; i++)
     v[i] = i;
    
    s = ''; 
    foreach(i in v)
      s = s + i + ' ';
    
    a = new List<|string|>[10];
    a[0] = new List<|string|>();
    a[0].Add('Hello');
    
    b = a[0].ToArray();
    c = b[0];
           

    递归(斐波纳契,最大公约数)

    function fib(n){
              if (n==1) return 1;
              else if (n==2) return 2;
              else return fib(n-1)+fib(n-2);
             }
    function fac(n){
              if (n==1) return 1;
              else return n*fac(n-1);
             }
    function GCD(a,b){
              if (a>b) return GCD(a-b,b);
              else
               if (b>a) return GCD(a,b-a);
              else
                return a;
             }

    关于动态语言

    一开始看Script.NET,和.NET的动态编译源代码很像,经过这两篇文章的学习,发现它已经远远超过了.NET动态编译的能力,从语法,编译处理到Host与Script之前的交互能力,应该与Iron,Python之类的动态语言相提并论。虽然文档不完整,有的部分已经过时(Obsolete),可以从它的UnitTest项目开始熟悉和了解它,Example项目文件夹也包含很多脚本例子。UnitTest项目既是单元测试的好工具,也是一份给开发人员看的文档,非常有价值。

  • 相关阅读:
    HDU 4285
    Codeforces 242C
    Codeforces 811C
    Codeforces 883H
    Codeforces 371D
    Codeforces 926E
    牛客算法周周练17 解题报告
    牛客算法周周练17D
    牛客算法周周练17C
    牛客算法周周练17A
  • 原文地址:https://www.cnblogs.com/JamesLi2015/p/2175405.html
Copyright © 2011-2022 走看看