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项目既是单元测试的好工具,也是一份给开发人员看的文档,非常有价值。

  • 相关阅读:
    python 的基础 学习 第六天 基础数据类型的操作方法 字典
    python 的基础 学习 第五天 基础数据类型的操作方法
    python 的基础 学习 第四天 基础数据类型
    ASP.NET MVC 入门8、ModelState与数据验证
    ASP.NET MVC 入门7、Hellper与数据的提交与绑定
    ASP.NET MVC 入门6、TempData
    ASP.NET MVC 入门5、View与ViewData
    ASP.NET MVC 入门4、Controller与Action
    ASP.NET MVC 入门3、Routing
    ASP.NET MVC 入门2、项目的目录结构与核心的DLL
  • 原文地址:https://www.cnblogs.com/JamesLi2015/p/2175405.html
Copyright © 2011-2022 走看看