程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> .NET 動態腳本語言Script.NET 應用舉例

.NET 動態腳本語言Script.NET 應用舉例

編輯:關於C#
 

類型定義 type definition

Script.NET不是OOP的語言,但可以借助於屬性(property bags)來模擬類型定義。

vector = [
  x -> 4,
  y -> 3,
   length -> function() {
       Math.Sqrt(me.x^2+me.y^2);
   } 
  ];

Console.WriteLine(vector.length());

這段代碼輸出的結果是5,這裡模擬向量類型的定義,並定義函數length計算向量的長度。me與C#中的this相似。

再來看下面的例子,同樣也是模擬OOP語言的類型定義

stack = [
             Test -> function() { return 'Hello Test';}
        ];
return stack.Test();

在Script.IDE中運行這段代碼,它的結果是輸出字符串Hello Test。

在property bags中,用->來指向定義的函數名稱,對比一下Script.NET語法規定的函數定義

function swap(array, a, b)
{
    tmp=array[a];
    array[a]=array[b];
    array[b]=tmp;
}

也可以這樣的語法形式定義函數,和JavaScript函數定義相似

Test = function (item) 
{
     y = item;
};
 

函數的作用域 Function Scopes

先看一下Script.NET官方文檔中的一張解釋scope的圖片

image

程序語言中scope的作用是解析名稱引用。比如,在C#中有如下的代碼

class Program
{
      static void Main(string[] args)
      {    
            int x=100;
            Add();           
      }

      static int Add()
      {
           int x=10;
           return x;
      }
}

通過運行代碼,可以知道Add方法(method,也可叫function)返回的值是10。C#是純粹pure的OOP語言,沒有全局變量。C語言則可以包涵全局變量,它在解析名稱引用(scope)方面會復雜很多。抱歉,對C語言相當陌生,不能舉例說明。

來看幾個例子,加深對scope的理解。

a = 4; b = 2; c = 3;
function test1(a,b)    global(c)
{
    c = a+b;
    a = 15;
}
Console.WriteLine(test1(2,3));
Console.WriteLine( a);
Console.WriteLine( b);
Console.WriteLine( c);

這次的函數定義稍微有些改變,在函數的參數列表之後,加了global(c) 以表示它的的作用域是global,全局的。

在Script.NET IDE中運行,結果如下

15
4
2
5

這裡先說明一下,函數如果沒有定義return語句,則最後一個表達式的值,就是函數的返回值,所以test1(2,3)返回a=15;結果就是15。a和b的scope是local,在function中對它的改變,不會影響到它的值(這和C語言的堆棧定義,調用函數,取參數的副本,並把參數壓入堆棧相同,如果要傳入參數變量的引用,而不是副本,則要用到指針)。c因為定義為global,在函數test1中對它的改變,仍然會保留,所以最後一行輸出結果5。

如果沒有忘記前面的contract部分的內容,應該可以理解下面的函數定義格式。

a = 4; b = 2; c = 3;
function test1(a,b)   global(c)
[
    pre(a>0);
    post();
    invariant();
]
    {
        c = a+b;
        a = 15;
    }

如果在函數中應用global,卻沒有定義它的變量,像下面的語句,則會拋出異常ScriptIdNotFoundException

Test = function (item) global(y)
     {
               y = item;
      };
Test(2);

在C語言中,一定要先定義函數,再來調用函數,否則會找不到函數定義。如果要在定義函數的語句之前調用該函數,則需要前置聲明。Visual C++中還要求在.h文件中聲明類型和函數,在.cpp文件中實現,C#中則沒有這個限制。

請看下面的代碼

return f();

function f() {return 1;}

在定義函數f的語句代碼之前,可以調用它而不用寫任何語句,多虧了編譯技術的進步。

 

運算符指派 Operators Dispatching

在程序語言中,如果運算符號(+,-%…)左右兩邊的類型不相同,則會發生類型轉換,這還涉及到運算符號重載,比如加號+可以應用到數據運算,也可以應用於字符串連接。請體會下面的例子來理解運算符指派。

Console.WriteLine( 1+1);                //2
Console.WriteLine( 1.2+1 );              //2.2  
Console.WriteLine( '1'+1 );              //11
Console.WriteLine( 'Hello '+1+' Text' ); //Hello 1 Text
Console.WriteLine( 10-1);       //9
Console.WriteLine( 1.2-1);      //1.2-1=0.2
Console.WriteLine( 10*12);      //10*12
Console.WriteLine( 3.2*3);      //(double)3.2*3=9.6
Console.WriteLine( 3.5*21.5);   //(double)3.5 * 21.5=75.25
Console.WriteLine( 6/2);        //6 / 2 =3
Console.WriteLine( 10/12);      //10 / 12 =0
Console.WriteLine( 45.43/12.3); //(double)45.43 / 12.3=.69349593495935
Console.WriteLine( 3.5/21);     //(double)3.5 / 21=0.166666666666667
Console.WriteLine( 3/21.2);     //3 / (double)21.2=0=0.141509433962264
Console.WriteLine( 6%2);        //6 % 2=0
Console.WriteLine( 10%12);      //10 % 12=10
Console.WriteLine( 45.43%12.3); //(double)45.43 % 12.3=8.53
Console.WriteLine( 6^2);        //Math.Pow(6, 2)=36    

如果運算符號不支持兩邊的數據類型,則發拋出異常NotSupportedException。比如

Console.WriteLine( '1'-1 );  //NotSupportedException

 

Scripting Runtime 運行時

Script.NET的作者畫了一張漂亮的Runtime圖,以解釋他們的作用。

Runtime

這個圖可以解釋下面的代碼,為什麼不需要添加類型Test的定義,也可以找到Test類型
//RuntimeHost.AddType("TestT", typeof(TestT));
Test rez = (Test)Script.RunCode(@"
         a = new Test();
         a.value = 'test';
         a.intVal = 20;

         return a; ")

原因就在AssemblyManager,它會sacn當前app domain中的程序集以及其中的類型定義,加入到Script Runtime中。

如果還記得前面文章中提到的RuntimeConfig.xml文件,它會在runtime 啟動時,加載需要的程序集,類型映射,和初試代碼片段。可以通過修改ScriptDotNet項目中的這個Embedded Resource的文件,達到初試化的目的。

image

也可以用下面的方法,加載自定義的runtime configuration。

public static Stream TestConfig
{
      get
      {
        Stream configStream = Assembly.GetExecutingAssembly().GetManifestResourceStream                                ("UnitTests.TestConfig.xml");
        configStream.Seek(0, SeekOrigin.Begin);
        return configStream;
      }
}

RuntimeHost.AssemblyManager = new BaseAssemblyManager();
RuntimeHost.Initialize(TestConfig);

RuntimeHost.Initialize();這一句是必須調用的,傳入Stream對象,還可以這樣調用

RuntimeHost.Initialize(new FileStream("RuntimeConfig.xml",FileMode.Open));

可以像下面的代碼定義,這樣阻止load程序集

RuntimeHost.AssemblyManager.BeforeAddAssembly +=
    (s, e) => {
       if (e.Assembly.FullName != "System.Data, Version=2.0.0.0, Culture=neutral,                                                            PublicKeyToken=b77a5c561934e089")
            throw new Exception();
        };
 

自定義運算符 Custom operator

再來看一個運算符的例子,我希望1234,$1234,都可以表示貨幣數量,$這個符號沒有內置在系統的運算符中,需要用下面的方法,添加進來。

private class DollarHandler : IOperatorHandler
{    
      public object Process(HandleOperatorArgs args)
      {
        if (args.Arguments != null && args.Arguments.Length == 1)
        {
          string value = (string)args.Arguments[0];
          args.Cancel = true;
          return int.Parse(value);
        }
        throw new NotSupportedException();
    }
}
 
RuntimeHost.Initialize(TestConfig);
RuntimeHost.RegisterOperatorHandler("$", new DollarHandler());

Script script = Script.Compile(@"
              return $'1234';   ");
object rez = script.Execute();

通過Debug代碼,可以看到rez的值是1234,沒有拋出異常。

 

擴展類型的函數 Custom type’s function

再看下面的例子,Script.NET內置的類型沒有ToString函數,我要給它擴展,添加ToString函數。

private class MyBinder : ObjectBinder
    {
      public override bool CanBind(MemberInfo member)
      {
          if (member.Name == "ToString")
              return true;         
        return base.CanBind(member);
      }
      public object ConvertTo(object value, Type targetType)
      {
          return value.ToString();
      }
}

再來舉例調用上面添加的函數

RuntimeHost.Binder = new MyBinder();
RuntimeHost.Initialize(TestConfig);

Script script = Script.Compile(@"
            b = 'a';
            b.ToString();
            money=100;
            money.ToString();
       ");
object obj=script.Execute();

返回結果obj的值是100。這使我想到了C#的擴展方法,如下所示

public class DecimalHelper
 {
        public static string ToString(this decimal obj)
        {
            return obj.ToString();
        }
 }

下面這樣的代碼是可以通過編譯的

decimal money = 100;
string howMuch=money.ToString();

這兩者非常的相似。C#中的decimal有對象System.Decimal來表達同等的含義,而Script.NET沒有對象的概念,只有基礎的數據類型double,long,string,bool和數組array,同樣可以做到擴展函數的功能。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved