一個精簡的C#表達式執行框架Dynamic Expresso

一、簡介

Dynamic Expresso是一個用.NET Standard 2.0編寫的簡單c#語句的解釋器。Dynamic Expresso嵌入了自己的解析邏輯,通過將其轉換為.NET lambda表達式或委托來解釋c#語句。

使用Dynamic Expresso開發人員可以創建可編寫腳本的應用程序,無需編譯即可執行.NET代碼,或者創建動態linq語句。

語句是使用c#語言規范的子集編寫的。全局變量或參數可以被注入并在表達式中使用。它不會生成程序集,但會動態地創建表達式樹。

二、安裝

新建控制臺項目DynamicExpressoResearch,并通過NUGet添加DynamicExpresso.Core;

三、功能和特性

  1. 返回值

    你可以解析并執行一個void類型沒有返回值的表達式,或者也可以返回任何有效的.NET類型。我們可以在解析表達式的時候指定期望的返回類型。

static void EvalVoidExp(){var target = new Interpreter();var t = target.Eval("Program.empty()", new Parameter("Program", typeof(Program), new Program()));if (t == null){Console.WriteLine("EvalVoidExp return value is null");}}//EvalVoidExp return value is null
static void EvalSpecReturnTypeExp(){var target = new Interpreter();double result = target.Eval<double>("Math.Pow(x, y) + 5",new Parameter("x", typeof(double), 10),new Parameter("y", typeof(double), 2));Console.WriteLine(string.Format("We specify the return value type of the expression as double, return value is {0}, value type is {1}", result, result.GetType().FullName));int r = target.Eval<int>("Math.Pow(x, y) + 5",new Parameter("x", typeof(int), 10),new Parameter("y", typeof(int), 2));Console.WriteLine(string.Format("We specify the return value type of the expression as int, return value is {0}, value type is {1}", r, r.GetType().FullName));}//We specify the return value type of the expression as double, return value is 105, value type is System.Double//We specify the return value type of the expression as int, return value is 105, value type is System.Int32
同時內置的表達式parser也可以自動感知表達式的返回類型;
static void AutoInferDataType(){var target = new Interpreter();object r = target.Eval("Math.Pow(x, y) + 5",new Parameter("x", typeof(int), 10),new Parameter("y", typeof(int), 2));Console.WriteLine(string.Format("We do not  specify the return value type of the expression, return value is {0}, value type is {1}", r, r.GetType().FullName));}

2.變量(Variables)

變量依附在Interpreter上,相當于一個全局的控制參數,可以應用到同一個Interpreter實例的所有表達式中;
Interpreter提供了不同的方法可以實現傳入不同類型的變量;
SetVariable可以內置的原始類型及復雜的自定義數據類型;
static void SetParameter(){var target = new Interpreter();target.SetVariable("rate", 0.8);object r = target.Eval("price * rate",new Parameter("price", typeof(int), 200));Console.WriteLine(string.Format("The price of 200 with a 20% discount is {0}", r));r = target.Eval("price * rate",new Parameter("price", typeof(int), 499));Console.WriteLine(string.Format("The price of 499 with a 20% discount is {0}", r));}The price of 200 with a 20% discount is 160The price of 499 with a 20% discount is 399.2
SetFunction可以通過委托的方式傳遞函數
static void SetFunction(){var target = new Interpreter();Func<double, double, double> pow = (x, y) => Math.Pow(x, y);target.SetFunction("pow", pow);object r = target.Eval("pow(x, y)",new Parameter("x", typeof(int), 10),new Parameter("y", typeof(int), 2));Console.WriteLine(string.Format("10 to the second power  is {0}", r));r = target.Eval("pow(x, y)",new Parameter("x", typeof(int), 2),new Parameter("y", typeof(int), 4));Console.WriteLine(string.Format("2 to the fourth power is {0}", r));}//10 to the second power  is 100//2 to the fourth power is 16
SetExpression可以設置自定義的Expression
static void SetExpression(){var target = new Interpreter();var rateExp = Expression.Constant(0.8);target.SetExpression("rate", rateExp);object r = target.Eval("price * rate",new Parameter("price", typeof(int), 200));Console.WriteLine(string.Format("The price of 200 with a 20% discount is {0}", r));r = target.Eval("price * rate",new Parameter("price", typeof(int), 499));Console.WriteLine(string.Format("The price of 499 with a 20% discount is {0}", r));}

3.參數(Parameters)

參數需要每次執行的時候傳遞,參數可以是任意的類型,我們可以只解析一次表達式,并通過不同的參數進行多次調用;
static void Invoke(){var target = new Interpreter();var parameters = new[] {new Parameter("x", typeof(int)),new Parameter("y", typeof(int))};var myFunc = target.Parse("x + y", parameters);myFunc.Invoke(23, 7);myFunc.Invoke(32, -2);            }

4.內置類型和自定義類型

默認內部直接支持的數據類型有
Object object Boolean bool Char charString stringSByte Byte byteInt16 UInt16 Int32 int UInt32 Int64 long UInt64 Single Double double Decimal decimal DateTime TimeSpanGuidMath Convert
我們可以直接使用Interpreter.Reference來引用任何的.net類型
static void ReferenceType(){var target = new Interpreter().Reference(typeof(Uri));Console.WriteLine((target.Eval("typeof(Uri)") as Type).FullName);Console.WriteLine(target.Eval("Uri.UriSchemeHttp"));}//System.Uri//http

5.生成動態委托

我們可以直接使用Interpreter.ParseAsDelegate<TDelegate>來解析表達式生成對應的委托,然后可以直接調用對應的委托。
class Customer{public string Name { get; set; }public int Age { get; set; }public char Gender { get; set; }}static void DynamicDelegate(){var customers = new List<Customer> {new Customer() { Name = "David", Age = 31, Gender = 'M' },new Customer() { Name = "Mary", Age = 29, Gender = 'F' },new Customer() { Name = "Jack", Age = 2, Gender = 'M' },new Customer() { Name = "Marta", Age = 1, Gender = 'F' },new Customer() { Name = "Moses", Age = 120, Gender = 'M' },};string whereExpression = "customer.Age > 18 && customer.Gender == 'F' && index >=0";var interpreter = new Interpreter();Func<Customer, int, bool> dynamicWhere = interpreter.ParseAsDelegate<Func<Customer, int, bool>>(whereExpression, "customer", "index");Console.WriteLine(customers.Where(dynamicWhere).Count());}

6.生成lambda表達式

我們可以使用Interpreter.ParseAsExpression<TDelegate>解釋表達式直接生成lambda表達式;
static void DynamicLambdaExpress(){var customers = new List<Customer> {new Customer() { Name = "David", Age = 31, Gender = 'M' },new Customer() { Name = "Mary", Age = 29, Gender = 'F' },new Customer() { Name = "Jack", Age = 2, Gender = 'M' },new Customer() { Name = "Marta", Age = 1, Gender = 'F' },new Customer() { Name = "Moses", Age = 120, Gender = 'M' },}.AsQueryable();string whereExpression = "customer.Age > 18 && customer.Gender == 'F' && index >=0";var interpreter = new Interpreter();Expression<Func<Customer, int, bool>> dynamicWhere = interpreter.ParseAsExpression<Func<Customer, int, bool>>(whereExpression, "customer", "index");Console.WriteLine(customers.Where(dynamicWhere).Count());}

7.支持的操作符

CategoryOperators
Primaryx.y f(x) a[x] new typeof
Unary+ - ! (T)x
Multiplicative* / %
Additive+ -
Relational and type testing< > <= >= is as
Equality== !=
Logical AND&
Logical OR
Logical XOR^
Conditional AND&&
Conditional OR
Conditional?:
Assignment=
Null coalescing??

8.文本標識

CategoryOperators
Constantstrue false null
Real literal suffixesd f m
Integer literal suffixesu l ul lu
String/char"" ''

字符串或者字符類型中支持的轉譯

  • ' - single quote, needed for character literals

  • " - double quote, needed for string literals

  • \ - backslash

  • \0 - Unicode character 0

  • \a - Alert (character 7)

  • \b - Backspace (character 8)

  • \f - Form feed (character 12)

  • \n - New line (character 10)

  • \r - Carriage return (character 13)

  • \t - Horizontal tab (character 9)

  • \v - Vertical quote (character 11)

9.調用對象成員

可以在表達式中直接調用對象實例的成員
public class Student{public string Name { get; set; }public int Age { get; set; }public void Hello(){Console.WriteLine(string.Format("hello, my name is {0}, my age is {1}", Name, Age));}public static Student Instance(){return new Student() { Name="auto", Age = 0};}}static void InvokTypeMember(){var s = new Student() { Name="mango", Age = 30};var target = new Interpreter().SetVariable("s", s);target.Reference(typeof(Student));Console.WriteLine(target.Eval("s.Hello()"));Console.WriteLine(target.Eval("s.Name"));Console.WriteLine(target.Eval("new Student().Hello()"));Console.WriteLine(target.Eval("Student.Instance().Hello()"));}
同時可以支持擴展方法的調用;
支持數組索引的調用;
支持params數組;
部分支持泛型;
static void InvokCollectionMember(){var x = new int[] { 10, 30, 4 };          var target = new Interpreter();target.SetVariable("x", x);Console.WriteLine(target.Eval("x.Count()"));//Console.WriteLine(target.Eval("x.Count<int>()"));Console.WriteLine(target.Eval("x[0]"));var s = new string[] {"mango", "is","test","params" };target.SetVariable("s", s);Console.WriteLine(target.Eval("string.Join(\" \", s)"));}

10.Lambda表達式

Dynamic Expresso只支持lambda表達式的部分功能;為了減少對性能的影響,默認是不開啟Lambda表達式的解析的;我們可以通過InterpreterOptions.LambdaExpressions 來啟用這個功能;
static void EvalAsLambda(){var x = new string[] { "this", "is", "awesome" };var options = InterpreterOptions.Default | InterpreterOptions.LambdaExpressions; // enable lambda expressionsvar target = new Interpreter(options).SetVariable("x", x);var results = target.Eval<IEnumerable<string>>("x.Where(str => str.Length > 5).Select(str => str.ToUpper())");Console.WriteLine(results.First());}

11.大小寫

默認情況下表達式是區分大小寫的,可以通過InterpreterOptions.CaseInsensitive或者InterpreterOptions.DefaultCaseInsensitive來忽略大小寫;
static void IgnorCase(){var target = new Interpreter(InterpreterOptions.CaseInsensitive);double x = 2;var parameters = new[] {new Parameter("x", x.GetType(), x)};Console.WriteLine(target.Eval("x", parameters));Console.WriteLine(target.Eval("X", parameters));}

12.標識符探測

Dynamic Expresso支持使用Interpreter.DetectIdentifiers來獲取表達式中的變量標識符。
static void DetectIdentifier(){var target = new Interpreter();var detectedIdentifiers = target.DetectIdentifiers("x + y");Console.WriteLine(detectedIdentifiers.UnknownIdentifiers.First());Console.WriteLine(detectedIdentifiers.UnknownIdentifiers.Last());}

13.默認數字類型

默認情況下數字會被解析成int類型或者double類型;但是在有些情況下,例如財務中需要使用decimal類型的數字;我們可以使用Interpreter.SetDefaultNumberType來設置;
static void SetNumberType(){var target = new Interpreter();target.SetDefaultNumberType(DefaultNumberType.Decimal);Console.WriteLine(target.Eval("45").GetType().FullName);Console.WriteLine(target.Eval("10/3"));}

14.異常

提供了ParseException作為解析表達式異常的基類,同時提供了數個不同的具體的異常類,例如UnknownIdentifierException, NoApplicableMethodException;

15.多線程

Interpreter類可以在多線程中使用,但是需要確保在多線程中只能調用get屬性和Parse及Eval這些方法,而對于初始化的一些設置(SetVariable/Reference)等只能在實例化對象的階段調用;如果我們需要使用不同的參數多次執行表達式,最好的方式就是解析一次表達式,并調用多次解析的結果;

16.安全性

表達式中只能訪問Reference中設置的類型以及設置的變量和參數的對象實例;我們需要慎重考慮暴露哪些類給用戶;從1.3版本其默認禁用直接調用除了Type.Name的反射類方法;

17.功能局限

Not every C# syntaxes are supported. Here some examples of NOT supported features:

  • Multiline expressions

  • for/foreach/while/do operators

  • Array/list/dictionary initialization

  • Explicit generic invocation (like method

    (arg))
  • Lambda/delegate declaration (delegate and lamda are only supported as variables or parameters or as a return type of the expression)

  • Array/list/dictionary element assignment (set indexer operator)

  • Other operations on dynamic objects (only property, method invocation and index now are supported)

18.使用場景

  • Programmable applications

  • Allow the user to inject customizable rules and logic without recompiling

  • Evaluate dynamic functions or commands

  • LINQ dynamic query

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/286592.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/286592.shtml
英文地址,請注明出處:http://en.pswp.cn/news/286592.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

算法馬拉松13 A-E解題報告

A題意&#xff08;取余最長路&#xff09;: 佳佳有一個n*m的帶權矩陣&#xff0c;她想從(1,1)出發走到(n,m)且只能往右往下移動&#xff0c;她能得到的娛樂值為所經過的位置的權的總和。 有一天&#xff0c;她被下了惡毒的詛咒&#xff0c;這個詛咒的作用是將她的娛樂值變為對p…

Modis數據處理工具:MRT百度網盤下載和手把手圖文安裝教程

如下圖所示為 MODIS Reprojection Tool(MRT)的軟界面,看似簡單,卻是Modis遙感影像必不可少的處理工具,如投影變換等。本文以圖文并茂的形式,詳細講解MRT軟件在Windows10平臺上的安裝過程,并附MRT軟件百度網盤下載地址。 Modis Tool主界面: 一、安裝過程 1、安裝Jav…

Word中如何設置圖片與段落的間距為半行

第一種&#xff1a; 正文為5號&#xff0c;那么圖片或者Viso對象前后空一行&#xff0c;設置字號為7號或者更小&#xff0c;這樣設置的間距就是那個7號字的間距&#xff0c;比5號小&#xff0c;看著空白不是那么大。 第二種&#xff1a; Visio對象轉為jpg&#xff0c;然后選中圖…

在微信小程序中使用“隨機鍵盤”

最近研究微信小程序&#xff0c;發現在手機上使用系統鍵盤非常不方便&#xff0c;一是按鍵太小&#xff0c;對于小學生來說&#xff0c;操作非常不方便&#xff1b;二是系統鍵盤反復切換影響界面布局。于是自己決定自己寫一個隨機的小鍵盤。 原理非常簡單&#xff1a;拿“口算練…

Android之提示訂閱配置訂閱需要傳新的包 添加結算權限。

1 問題 apk上google應用市場&#xff0c;然后開通支付商品&#xff0c;錯誤提示如下 2 解決辦法 AndroidManifest.xml里面添加谷歌支付權限 <!-- google pay --><uses-permission android:name"com.android.vending.BILLING" />

【前端就業課 第一階段】HTML5 零基礎到實戰(三)一篇文CSS基礎入門

注意&#xff1a;手機&#xff08;APP&#xff09;打開&#xff0c;內容顯示更佳&#xff0c;不會的私聊博主即可 想要拿代碼或加入學習計劃&#xff08;** 博主會監督你并且教你寫文章 **&#xff09;的拉到最下面&#xff08;PC端Web打開&#xff09;加博主即可&#xff0c;目…

C#如何獲取實體類屬性名和值?

數據模型定義public class User{public User(){student new student();}public string name { get; set; }public string gender { get; set; }public int age { get; set; }public student student { get; set; }}public class student{public int ID { get; set; }public st…

將VNC 安裝在Centos 7步驟

&#xff08; Virtual Network Computing&#xff09;VNC允許Linux系統可以類似實現像Windows中的遠程桌面訪問那樣訪問Linux桌面。本文配置機器是興寧市網絡信息中心的一臺Centos 7 HP服務器環境下運行。 首先試試服務器裝了VNC沒 [rootwic ~]# rpm -q tigervnc tigervnc-serv…

利用MRT進行Modis NDVI數據(MOD13Q1)投影變換格式轉換操作圖文教程

本實例以Modis NDVI(MOD13Q1,空間分辨率為250m)一景影像數據為例,演示利用MRT進行Modis NDVI影像變換,主要內容包括:將.hdf格式轉為.tif格式,將坐標系轉為Albers等積投影。 ArcGIS完美轉換方法: 《ArcGIS10.8完美實現MODIS NDVI數據格式轉換和投影變換》 《重磅!ArcGI…

ActiveMQ無法啟動

解決辦法&#xff1a;activemq無法啟動&#xff0c;端口被占用 用netstat -an無法查出61616被哪個進程占用&#xff08;實踐證明&#xff0c;netstat -ano|findstr 61616什么也沒有找到&#xff09; 經過排查和網上資料參考&#xff0c;被windows的Internet connection share(I…

Android之升級OkHttp編譯提示錯誤如下Using ‘body(): ResponseBody?’ is an error. moved to val

1 問題 升級okHttp庫&#xff0c;編譯項目錯誤如下 Using ‘body(): ResponseBody?’ is an error. moved to val 2 解決辦法 原來的代碼 val list response.body().string() 去掉&#xff08;&#xff09;就可以了 val list response.body.string()

單例

當實際上Singleton是一個對象&#xff0c;我們不能保證使用者不會使用其他的方法去創建&#xff08;比如alloc&#xff09;,這個時候他就會創建多個實例&#xff0c;這樣就會出現這些無法感知的bug&#xff09; implementation Singleton static Singleton * sharedSingleton …

Google 開源的 Android 排版庫:FlexboxLayout

最近Google開源了一個項目叫「FlexboxLayout」。1.什么是 Flexbox簡單來說 Flexbox 是屬于web前端領域CSS的一種布局方案&#xff0c;是2009年W3C提出了一種新的布局方案&#xff0c;可以簡便、完整、響應式地實現各種頁面布局&#xff0c;并且 React Native 也是使用的 Flex 布…

Docker Network 配置,自定義bridge網絡

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182Docker Network 配置&#xff0c;自定義bridge網絡 1.停止服務 service docker stop 2.關掉docker0 ifconfi…

再見 KataCoda——O'Reilly 宣布其將在六月份關閉

近日聽聞 OReilly 將永久關閉在線學習網站 KataCoda&#xff0c;對于廣大程序員和學習者來說&#xff0c;這無疑是一件痛心疾首的事情&#xff0c;以后我們再也看不到那只會變成的功夫貓了。KataCoda 簡介KataCoda 成立于 2016 年&#xff0c;它是一個在線學習平臺&#xff0c;…

中國區域Modis行列號(附Shapefile文件下載)

重磅&#xff1a;Landsat中國西北地區行列號Shapefile圖層對照&#xff08;附行列號Shapefile下載&#xff09; 全球&#xff1a; 中國&#xff1a;

Android之解決webview加載第三方網頁點擊彈不出下拉框(html頁面里面的select標簽)

1 問題 決webview加載第三方網頁點擊彈不出下拉框&#xff08;html頁面里面的select標簽&#xff09;&#xff0c;我們訪問youtube.com官網&#xff0c;點擊網站的視頻&#xff0c;點擊視頻右上角三個點設置&#xff0c;然后點擊 播放設置 然后點擊畫質 彈不出選項框&#xf…

【前端就業課 第一階段】HTML5 零基礎到實戰(四)偽類與偽元素

注意&#xff1a;手機&#xff08;APP&#xff09;打開&#xff0c;內容顯示更佳&#xff0c;不會的私聊博主即可 想要拿代碼或加入學習計劃&#xff08;** 博主會監督你并且教你寫文章 **&#xff09;的拉到最下面&#xff08;PC端Web打開&#xff09;加博主即可&#xff0c;目…

編寫第一個響應式頁面

2019獨角獸企業重金招聘Python工程師標準>>> 本文為大家講解如何使用一種科學的方法實現網頁設計&#xff0c;從原理上搞清楚什么是響應式設計&#xff0c;并實現一個簡易的響應式設計框架&#xff0c;以此為基礎&#xff0c;編寫出第一個響應式頁面。 不知道現在大…

container 的背后

如果要看laravel的單個功能的源代碼&#xff0c;首先去找對應得ServiceProvider,例如加密功能hash,則按一下步驟查看源代碼&#xff1a; HashServiceProvider.php(主要是看register方法) singleton()方法就是將BcryptHasher這個類實例化一次&#xff0c;然后在哪里都可以用&…