zoukankan      html  css  js  c++  java
  • Dynamic Expresso--一个精简的C#表达式执行框架

    一、简介

    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 160
            The 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 char
        String string
        SByte Byte byte
        Int16 UInt16 Int32 int UInt32 Int64 long UInt64 
        Single Double double Decimal decimal 
        DateTime TimeSpan
        Guid
        Math 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.支持的操作符

    Category Operators
    Primary x.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.文本标识

    Category Operators
    Constants true false null
    Real literal suffixes d f m
    Integer literal suffixes u l ul lu
    String/char "" ''

    字符串或者字符类型中支持的转译

    • ' - single quote, needed for character literals
    • " - double quote, needed for string literals
    • - backslash
    • - Unicode character 0
    • a - Alert (character 7)
    •  - Backspace (character 8)
    • f - Form feed (character 12)
    • - New line (character 10)
    • - Carriage return (character 13)
    • - 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 expressions
                var 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
  • 相关阅读:
    mysql 安装命令
    MySQL——修改root密码的4种方法(以windows为例)
    正则表达式(一):php常用的正则匹配
    nginx+php在调试过程中临时关闭缓存
    (总结)Nginx配置文件nginx.conf中文详解
    理解Linux系统/etc/init.d目录和/etc/rc.local脚本
    关于mongodb ,redis,memcache之间见不乱理还乱的关系和作用
    angularjs factory,service,provider 自定义服务的不同
    使用loopback创建nodejs框架
    采用express创建nodejs服务器
  • 原文地址:https://www.cnblogs.com/wufengtinghai/p/15479486.html
Copyright © 2011-2022 走看看