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

posted @ 2011-09-14 09:15  信息化建设  阅读(7177)  评论(7编辑  收藏  举报