[转]C#通过Expression实现 字符串转表达式 自定义计算体积参数

(转载请删除括号里的内容)

Expression实现 字符串转表达式 自定义计算体积参数


这篇文章是为了解决我工作中的一个需求而写,希望能帮到有需要的人

需求的这样的:
业务想让用户输入任意表达式进行计算假如成立则允许操作

表达式组成:
常量与特殊变量进行任意加减乘除有特殊值比较大小

表达式实例:

  1. L+2*W<200
  2. H>50
  3. L/W+H*20-10>=100

调用方法

ExpressionVolume expressionVolume = new ExpressionVolume();

            //计算表达式的值
            Console.WriteLine(expressionVolume.InvokeOperator("L+10+2*W/10+H+200-800/20+L+W>=200", new ExpressionVolume.VolumeClass(10, 50, 100)));
            Console.WriteLine(expressionVolume.InvokeOperator("L+10+2*W/10+H+200-800/4+L+H/W>=200", new ExpressionVolume.VolumeClass(1, 1, 1)));

            Console.WriteLine((decimal)10 + (decimal)10 + (decimal)2 * (decimal)50 / (decimal)10 + (decimal)100 + (decimal)200 - (decimal)800 / (decimal)20 + (decimal)10 + (decimal)50);
            Console.WriteLine((decimal)1 + (decimal)10 + (decimal)2 * (decimal)1 / (decimal)10 + (decimal)1 + (decimal)200 - (decimal)800 / (decimal)4 + (decimal)1 + (decimal)1 / (decimal)1);

            //比较表达式是否成立
            Console.WriteLine(expressionVolume.InvokeEqual("L+10+2*W/10+H+200-800/20+L+W<200", new ExpressionVolume.VolumeClass(10, 50, 100)));
            Console.WriteLine(expressionVolume.InvokeEqual("L+10+2*W/10+H+200-800/4+L+H/W<200", new ExpressionVolume.VolumeClass(1, 1, 1)));

            //验证表达式是否成立(!=-1表示成立)
            Console.WriteLine(expressionVolume.Verification("L+10+2*W/10+H+200-800/20+L+W<200")); 
            Console.WriteLine(expressionVolume.Verification("W+10+2*W/10+H+200-800/20+W+W<200"));
            Console.WriteLine(expressionVolume.Verification("L+2+2*-W/10+H+200-800/20+L+W<=200"));
            Console.WriteLine(expressionVolume.Verification("-L+33+2/*W/10+H+200-800/20+L+W>=200"));
            Console.WriteLine(expressionVolume.Verification("L+10-+2*W/10+H+200-800/20+L+W"));
            Console.WriteLine(expressionVolume.Verification("L++10+2*W/10+H+200-800/20+L+W"));
            expressionVolume.Verification("L+10+2*W/10+HL+200-800/20+L+W<200");

代码实现

/// <summary>
    /// 计算体积参数
    /// </summary>
    public class ExpressionVolume
    {
        #region 私有类
        /// <summary>
        /// 体积参数类
        /// </summary>
        public class VolumeClass
        {
            /// <summary>
            /// 不可以去掉
            /// Expression合成方法时需要支持无参实例化
            /// </summary>
            public VolumeClass()
            {

            }

            /// <summary>
            /// 实例化
            /// </summary>
            /// <param name="Long">长</param>
            /// <param name="Width">宽</param>
            /// <param name="Height">高</param>
            public VolumeClass(decimal? Long, decimal? Width, decimal? Height, decimal? Wt_Weight = null)
            {
                this.L = Long ?? 0;
                this.W = Width ?? 0;
                this.H = Height ?? 0;
                this.Wt_Weight = Wt_Weight ?? 0;
            }

            /// <summary>
            /// 长
            /// </summary>
            /// <remarks>
            /// 不能随意改变字段名
            /// Expression合成方法时需要
            /// </remarks>
            public decimal L { get; set; }

            /// <summary>
            /// 宽
            /// </summary>
            /// <remarks>
            /// 不能随意改变字段名
            /// Expression合成方法时需要
            /// </remarks>
            public decimal W { get; set; }

            /// <summary>
            /// 高
            /// </summary>
            /// <remarks>
            /// 不能随意改变字段名
            /// Expression合成方法时需要
            /// </remarks>
            public decimal H { get; set; }

            /// <summary>
            /// 重量
            /// </summary>
            public decimal Wt_Weight { get; set; }
        }
        #endregion

        /// <summary>
        /// 线程锁,防止缓存键值对时异常
        /// </summary>
        private readonly static object Lock = new object { };

        /// <summary>
        /// 缓存表达式(比较)
        /// </summary>
        private readonly static Dictionary<string, Func<VolumeClass, bool>> DicEqual = new Dictionary<string, Func<VolumeClass, bool>>();

        /// <summary>
        /// 比较表达式是否成立
        /// </summary>
        /// <param name="expressionString">表达式</param>
        /// <param name="volume">体积参数</param>
        /// <returns></returns>
        public bool InvokeEqual(string expressionString, VolumeClass volume)
        {
            if (DicEqual.ContainsKey(expressionString))
            {
                return DicEqual[expressionString].Invoke(volume);
            }

            var strs = expressionString.Split('>', '<');

            if (strs.Length != 2)
            {
                DicEqual.Add(expressionString, t => false);
                return false;
            }

            //定义体积参数
            ParameterExpression parameterExpression = Expression.Parameter(typeof(VolumeClass), "volume");
            Expression expression = GetExpression(strs[0], parameterExpression);

            //是否大于
            bool IsGreaterThan = expressionString.Contains('>');

            //是否允许相等
            bool IsEqual = strs[1].StartsWith("=");
            strs[1] = strs[1].TrimStart('=');

            decimal Equal = decimal.Parse(strs[1]);

            if (IsGreaterThan)
            {
                if (IsEqual)
                {
                    expression = Expression.GreaterThanOrEqual(expression, Expression.Constant(Equal, typeof(decimal)));
                }
                else
                {
                    expression = Expression.GreaterThan(expression, Expression.Constant(Equal, typeof(decimal)));
                }
            }
            else
            {
                if (IsEqual)
                {
                    expression = Expression.LessThanOrEqual(expression, Expression.Constant(Equal, typeof(decimal)));
                }
                else
                {
                    expression = Expression.LessThan(expression, Expression.Constant(Equal, typeof(decimal)));
                }
            }
            //获得Lambda表达式
            Expression<Func<VolumeClass, bool>> Lambda = Expression.Lambda<Func<VolumeClass, bool>>(expression, parameterExpression);

            //生成对应的委托
            var func = Lambda.Compile();

            //防止并发导致异常
            if (!DicEqual.ContainsKey(expressionString))
            {
                lock (Lock)
                {
                    if (!DicEqual.ContainsKey(expressionString))
                    {
                        //缓存委托
                        DicEqual.Add(expressionString, func);
                    }
                }
            }

            return func.Invoke(volume);
        }

        /// <summary>
        /// 缓存表达式(计算值)
        /// </summary>
        private readonly static Dictionary<string, Func<VolumeClass, decimal>> DicOperator = new Dictionary<string, Func<VolumeClass, decimal>>();

        /// <summary>
        /// 得到表达式左侧的值
        /// </summary>
        /// <param name="expressionString">表达式</param>
        /// <param name="volume">体积参数</param>
        /// <returns></returns>
        public decimal InvokeOperator(string expressionString, VolumeClass volume)
        {
            if (DicOperator.ContainsKey(expressionString))
            {
                return DicOperator[expressionString].Invoke(volume);
            }

            var strs = expressionString.Split('>', '<');

            if (strs.Length != 2)
            {
                DicOperator.Add(expressionString, t => -1);
                return -1;
            }

            //定义体积参数
            ParameterExpression parameterExpression = Expression.Parameter(typeof(VolumeClass), "volume");
            System.Linq.Expressions.Expression expression = GetExpression(strs[0], parameterExpression);

            //获得Lambda表达式
            Expression<Func<VolumeClass, decimal>> Lambda = Expression.Lambda<Func<VolumeClass, decimal>>(expression, parameterExpression);

            //生成对应的委托
            var func = Lambda.Compile();

            //防止并发导致异常
            if (!DicOperator.ContainsKey(expressionString))
            {
                lock (Lock)
                {
                    if (!DicOperator.ContainsKey(expressionString))
                    {
                        //缓存委托
                        DicOperator.Add(expressionString, func);
                    }
                }
            }
            return func.Invoke(volume);
        }

        /// <summary>
        /// 验证表达式
        /// </summary>
        /// <param name="expressionString">表达式</param>
        /// <returns></returns>
        public decimal Verification(string expressionString)
        {
            var strs = expressionString.Split('>', '<');

            if (strs.Length != 2)
            {
                DicOperator.Add(expressionString, t => -1);
                return -1;
            }
            if (string.IsNullOrEmpty(strs[1]))
            {
                return -1;
            }

            //定义体积参数
            ParameterExpression parameterExpression = Expression.Parameter(typeof(VolumeClass), "volume");
            System.Linq.Expressions.Expression expression = GetExpression(strs[0], parameterExpression);

            //获得Lambda表达式
            Expression<Func<VolumeClass, decimal>> Lambda = Expression.Lambda<Func<VolumeClass, decimal>>(expression, parameterExpression);

            //生成对应的委托
            var func = Lambda.Compile();

            return func.Invoke(new VolumeClass(1, 1, 1));
        }

        #region 私有方法
        /// <summary>
        /// 生成公共表达式
        /// </summary>
        /// <param name="str"></param>
        /// <param name="parameterExpression">定义体积参数</param>
        /// <returns></returns>
        private System.Linq.Expressions.Expression GetExpression(string str, ParameterExpression parameterExpression)
        {
            if (str.StartsWith("+") || str.StartsWith("-") || str.StartsWith("*") || str.StartsWith("/"))
            {
                throw new Exception("表达式不能已符号开头");
            }
            Expression expression = null;
            foreach (var Add in str.Split('+'))
            {
                if (Add.Contains('-'))
                {
                    //假如是第一次进入则表示还是+
                    bool IsFirst = true;
                    foreach (var Subtract in Add.Split('-'))
                    {
                        Expression _expression = null;
                        if (Subtract.Contains('*'))
                        {
                            _expression = MultiplyAction(Subtract, parameterExpression);
                        }
                        else if (Subtract.Contains('/'))
                        {
                            _expression = DivideAction(Subtract, parameterExpression);
                        }
                        else
                        {
                            _expression = GetPropertyOrConstant(Subtract, parameterExpression);
                        }
                        if (IsFirst)
                        {
                            expression = expression == null ? _expression : Expression.Add(expression, _expression);
                        }
                        else
                        {
                            //不是第一次进入不可能是null
                            expression = Expression.Subtract(expression, _expression);
                        }
                        IsFirst = false;
                    }
                }
                else if (Add.Contains('*'))
                {
                    Expression _expression = MultiplyAction(Add, parameterExpression);
                    expression = expression == null ? _expression : Expression.Add(expression, _expression);
                }
                else if (Add.Contains('/'))
                {
                    Expression _expression = DivideAction(Add, parameterExpression);
                    expression = expression == null ? _expression : Expression.Add(expression, _expression);
                }
                else
                {
                    //拼接+
                    Expression _expression = GetPropertyOrConstant(Add, parameterExpression);
                    expression = expression == null ? _expression : Expression.Add(expression, _expression);
                }
            }

            return expression;
        }

        /// <summary>
        /// 乘
        /// </summary>
        /// <param name="str"></param>
        private System.Linq.Expressions.Expression MultiplyAction(string str, ParameterExpression parameterExpression)
        {
            List<Expression> list = new List<Expression>();
            foreach (var Multiply in str.Split('*'))
            {
                list.Add(Multiply.Contains('/') ? DivideAction(Multiply, parameterExpression) : GetPropertyOrConstant(Multiply, parameterExpression));
            }
            Expression MultiplyExpression = list[0];
            for (int i = 1; i < list.Count; i++)
            {
                MultiplyExpression = Expression.Multiply(MultiplyExpression, list[i]);
            }
            return MultiplyExpression;
        }

        /// <summary>
        /// 除
        /// </summary>
        /// <param name="str"></param>
        private System.Linq.Expressions.Expression DivideAction(string str, ParameterExpression parameterExpression)
        {
            string[] strs = str.Split('/');
            return Expression.Divide(GetPropertyOrConstant(strs[0], parameterExpression), GetPropertyOrConstant(strs[1], parameterExpression));
        }

        /// <summary>
        /// 获取属性或常量
        /// </summary>
        /// <param name="value">字符串</param>
        /// <param name="parameterExpression">变量参数</param>
        /// <returns></returns>
        private System.Linq.Expressions.Expression GetPropertyOrConstant(string value, ParameterExpression parameterExpression)
        {
            if (string.IsNullOrEmpty(value))
            {
                throw new Exception("符号不能相连");
            }
            Expression expression = null;
            if (value == "L" || value == "W" || value == "H")
            {
                //读取变量的属性
                expression = Expression.Property(parameterExpression, value);
            }
            else
            {
                try
                {
                    expression = Expression.Constant(decimal.Parse(value), typeof(decimal));
                }
                catch (Exception)
                {
                    throw new Exception("长宽高不能相连");
                }
            }
            return expression;
        }
        #endregion
    }

实体类赋值(与本文章无关内容放到这边方便复制)

/// <summary>
   /// 不支持DataRow  泛型缓存
   /// </summary>
   /// <typeparam name="Tout">返回类型</typeparam>
   /// <typeparam name="Tin">接受类型</typeparam>
   public class ModelCopy<Tin, Tout>
   {
       private static Func<Tin, Tout> func { get; set; }//静态缓存字段

       /// <summary>
       /// 赋值
       /// </summary>
       /// <param name="model"></param>
       /// <returns></returns>
       public static Tout Invoke(Tin model)//公共方法,返回Map到的对象实例
       {
           #region 为Func赋值
           if (func == null)
           {
               Type inType = typeof(Tin);
               Type outType = typeof(Tout);

               ParameterExpression parameterExpression = Expression.Parameter(inType, "tIn");//准备一个参数表达式
               List<MemberBinding> memberBindings = new List<MemberBinding>();//准备属性绑定的集合
               foreach (var item in outType.GetProperties())//遍历所有的属性
               {
                   PropertyInfo propertyInfo = inType.GetProperty(item.Name);//获取到传入实例中的属性
                   if (propertyInfo == null)
                       continue;
                   MemberExpression memberExpression = Expression.Property(parameterExpression, propertyInfo);//准备一个成员的表达式
                   MemberBinding memberBinding = Expression.Bind(item, memberExpression);//绑定
                   memberBindings.Add(memberBinding);//添加到列表
               }

               //字段
               //foreach (var item in outType.GetFields())
               //{
               //    FieldInfo fieldInfo = inType.GetField(item.Name);
               //    if (fieldInfo == null)
               //        continue;
               //    MemberExpression memberExpression = Expression.Field(parameterExpression, fieldInfo);
               //    MemberBinding memberBinding = Expression.Bind(item, memberExpression);
               //    memberBindings.Add(memberBinding);
               //}

               MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(outType), memberBindings.ToArray());
               Expression<Func<Tin, Tout>> lambdaExpression = Expression.Lambda<Func<Tin, Tout>>(memberInitExpression, new ParameterExpression[] { parameterExpression });
               func = lambdaExpression.Compile();//表达式编译为委托
           }
           #endregion

           return func.Invoke(model);
       }
   }


---------------------
作者:樱桃丶老丸子
来源:CSDN
原文:https://blog.csdn.net/m0_48365841/article/details/121380411
版权声明:本文为作者原创文章,转载请附上博文链接!
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值