C# 实现自定义表达式

最近遇到一个问题,希望程序可以支持自定义表达式的计算,
比如A(p1) & B(p2,p3) || C(),计算这个表达式是true还是false,进而去完成其他工作
而到运行到这里的时候,程序才会知道具体的表达式是什么,表达式和参数是通过定义表达式的数据传进来的,是不固定的
数据结构类似

internal class CustomCondition
{
    internal MethodTypeEnum MethodType { get; set; }
    internal ConnectionTypeEnum ConnectionType { get; set; }
    internal object Value { get; set; }
}
internal enum MethodTypeEnum
{
    Method1,
    Method2,
}
internal enum ConnectionTypeEnum
{
    Add,
    Or
}

这里没有加(),实际上也可能有括号,比如(A(p1) && B(p2,p3)) || C() && D(p4)

我最开始做的版本,是把每一个方法的结果算出来,然后再根据 && 和 || 做运算,最后得到结论,
但实际上,并不是每一个方法都需要被运算,才能得到表达式的结果
比如,A() || B(),当计算A()为true时,表达式结果就是true,不必再计算B()方法了

解决这个问题,能用到C#的短路模式是最佳的,
一开始我的思路是通过表达式树来实现,但是因为条件可能会很复杂,调研之后觉得要写的代码太多了,Debug也会很麻烦,
最后选择了一个第三方库DynamicExpresso
阅读README可知,通过“拼接”表达式,传入参数的方式,可以快速实现目标
关键代码如下

List<CustomCondition> customConditions = new List<CustomCondition>();
customConditions.Add(new CustomCondition() { MethodType = MethodTypeEnum.Method2, ConnectionType = ConnectionTypeEnum.Or, Value = 0 });
customConditions.Add(new CustomCondition() { MethodType = MethodTypeEnum.Method1, ConnectionType = ConnectionTypeEnum.Or, Value = "not null" });

bool result = false;
string expressionText = "";
List<Parameter> parameters = new List<Parameter>();
int index = 0;
foreach (CustomCondition condition in customConditions)
{
    var parameterName = $"parameter{index}";
    switch (condition.MethodType)
    {
        case MethodTypeEnum.Method1:
            expressionText += $"{nameof(Method1)}({parameterName}) {GetConnectionType(condition.ConnectionType)} ";
            parameters.Add(new Parameter(parameterName, typeof(string), (string)condition.Value));
            break;
        case MethodTypeEnum.Method2:
            expressionText += $"{nameof(Method2)}({parameterName}) {GetConnectionType(condition.ConnectionType)} ";
            parameters.Add(new Parameter(parameterName, typeof(int), (int)condition.Value));
            break;
        default:
            throw new Exception("错误的MethodType");
    }
    index++;
}
expressionText = expressionText.Remove(expressionText.Length - 3);

var target = new Interpreter().SetFunction(nameof(Method1), Method1).SetFunction(nameof(Method2), Method2);

var r3 = target.Eval<bool>(expressionText, parameters.ToArray());

示例代码

CustomExpressionTestDemo

posted @ 2022-03-29 15:15  Lulus  阅读(692)  评论(0编辑  收藏  举报