最近遇到一个问题,希望程序可以支持自定义表达式的计算,
比如​​​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​