You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将含逻辑运算的字符串转为LINQ表达式验证集合有效性?

实现思路:把规则字符串转成LINQ可执行的校验逻辑

这个需求本质上是要实现一个简单的布尔表达式解析器,把字符串形式的规则转换成能直接和集合A做校验的逻辑。我来一步步拆解实现步骤:

第一步:预处理输入集合A

首先把字符串A转换成HashSet<string>,因为后续要频繁做「元素是否存在」的检查,HashSet的Contains方法效率远高于普通List。代码示例:

string A = "ab1, eb4, gr3";
var allowedItems = new HashSet<string>(
    A.Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries)
);

第二步:解析布尔规则字符串(B和C)

核心是把类似"(ut2 | eg1) & ab1"的中缀表达式,转换成可执行的布尔逻辑。这里分三个小步骤:

2.1 分词(Tokenize)

先把规则字符串拆成操作数(如ab1)、运算符(&/|)、括号这些独立的「令牌」,方便后续处理:

private static List<string> Tokenize(string expression)
{
    var tokens = new List<string>();
    var currentToken = new StringBuilder();
    
    foreach (var c in expression)
    {
        if (char.IsWhiteSpace(c)) continue;
        
        // 遇到括号或运算符,先把当前累积的操作数加入列表
        if (c == '(' || c == ')' || c == '&' || c == '|')
        {
            if (currentToken.Length > 0)
            {
                tokens.Add(currentToken.ToString());
                currentToken.Clear();
            }
            tokens.Add(c.ToString());
        }
        else
        {
            currentToken.Append(c);
        }
    }
    
    // 把最后一个操作数加入列表
    if (currentToken.Length > 0) tokens.Add(currentToken.ToString());
    return tokens;
}

2.2 转成后缀表达式(逆波兰式)

中缀表达式带括号,直接计算逻辑很麻烦,用Shunting-yard算法把它转成后缀表达式(逆波兰式),就能用栈轻松计算:

private static List<string> ToPostfix(List<string> tokens)
{
    var output = new List<string>();
    var stack = new Stack<string>();
    // 定义运算符优先级:& 高于 |
    var precedence = new Dictionary<string, int> { { "&", 2 }, { "|", 1 } };

    foreach (var token in tokens)
    {
        if (token == "(")
        {
            stack.Push(token);
        }
        else if (token == ")")
        {
            // 弹出栈内元素直到遇到左括号
            while (stack.Peek() != "(")
            {
                output.Add(stack.Pop());
            }
            stack.Pop(); // 弹出左括号,不加入输出
        }
        else if (precedence.ContainsKey(token))
        {
            // 弹出栈内优先级更高或相等的运算符
            while (stack.Count > 0 && stack.Peek() != "(" && precedence[stack.Peek()] >= precedence[token])
            {
                output.Add(stack.Pop());
            }
            stack.Push(token);
        }
        else
        {
            // 操作数直接加入输出列表
            output.Add(token);
        }
    }
    
    // 把栈内剩余的运算符加入输出
    while (stack.Count > 0) output.Add(stack.Pop());
    return output;
}

2.3 计算后缀表达式结果

用栈遍历后缀表达式,每个操作数对应检查集合A是否包含它,然后根据运算符执行布尔运算:

private static bool EvaluatePostfix(List<string> postfix, HashSet<string> allowedItems)
{
    var stack = new Stack<bool>();
    
    foreach (var token in postfix)
    {
        if (token == "&")
        {
            var b = stack.Pop();
            var a = stack.Pop();
            stack.Push(a && b);
        }
        else if (token == "|")
        {
            var b = stack.Pop();
            var a = stack.Pop();
            stack.Push(a || b);
        }
        else
        {
            // 检查当前项是否在A的集合中
            stack.Push(allowedItems.Contains(token));
        }
    }
    
    return stack.Pop();
}

第三步:整合校验逻辑

把必选规则B和禁用规则C的结果结合起来:必选规则必须满足,禁用规则必须不满足。完整调用示例:

// 预处理A
string A = "ab1, eb4, gr3";
var allowedItems = new HashSet<string>(
    A.Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries)
);

// 校验必选规则B
string B = "(ut2 | eg1) & ab1";
var bTokens = Tokenize(B);
var bPostfix = ToPostfix(bTokens);
bool meetsRequired = EvaluatePostfix(bPostfix, allowedItems);

// 校验禁用规则C
string C = "ke2 & pa5";
var cTokens = Tokenize(C);
var cPostfix = ToPostfix(cTokens);
bool violatesForbidden = EvaluatePostfix(cPostfix, allowedItems);

// 最终校验结果:符合必选规则 且 不违反禁用规则
bool isValid = meetsRequired && !violatesForbidden;

进阶优化点

  • 如果需要和EF等ORM结合,可以把EvaluatePostfix改成构建Expression<Func<HashSet<string>, bool>>,这样表达式可以被翻译成SQL执行。
  • 加入错误处理:比如校验不匹配的括号、非法运算符、不存在的操作数等,避免程序崩溃。
  • 扩展支持!(非)运算符,满足更复杂的规则需求。

内容的提问来源于stack exchange,提问作者fox

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 22:37:28