【问题标题】:LINQ Boolean expressionsLINQ 布尔表达式
【发布时间】:2020-06-09 10:27:26
【问题描述】:

我不知道这是否是正确的问题。假设我有一个带值的字符串:

A = "ab1, eb4, gr3, ..."

然后还有两个字符串B 包含必须具有的值C 不允许的值。不幸的是,B 和 C 可以在字符串中包含逻辑 AND / OR

例如

B = "(ut2 | eg1) & ab1"

C = "ke2 & pa5"

现在我正在寻找一种方法来检查 A 对 B 和 C 是否有效。真假。

对于 A,将字符串转换为包含单独值的字符串列表并在该列表上启用 LINQ 表达式非常容易。

但我怎样才能最好地“解析” B 和 C 中的信息,以使用 List A 上的 LINQ 表达式对其进行评估?

认为理想的解决方案是将 B 和 C 分别作为一个逻辑表达式来直接检查列表 A。但是如何开始呢?

感谢您的支持和问候。

【问题讨论】:

  • 听起来您正在寻找与 OData 过滤方式非常相似的东西。
  • 请让我补充一点,它必须最终成为除 VB.net 或 C# 代码之外的软件解决方案,但无法导入库。所以我正在寻找一种具有框架功能的方法。
  • 我不确定您为什么希望使用 LINQ 来完成此任务。您可能应该只考虑制作自己的函数来评估它
  • 这不是一个小问题;您必须以某种方式解析逻辑表达式。当然可能,但并不简单。如果您正在寻找神奇的 linq one 班轮,那么您很不走运,这不会发生。
  • B 和 C 能有多复杂?另外,是否有 Not 运算符?正如所指出的,这不是微不足道的。

标签: c# .net vb.net linq lambda


【解决方案1】:

StackOverflow 不是代码编写服务,但有些代码编写起来很有趣。

这是一个将测试短语解析成Expression树然后编译树返回Func来测试HashSet<string>的类:

public static class PhraseCompiler {
    public static Regex tokenRE = new Regex(@"\(|\)|&|\||\w+", RegexOptions.Compiled);
    static IEnumerable<string> Tokens(string phrase) {
        foreach (Match m in tokenRE.Matches(phrase))
            yield return m.Value;
    }

    static MethodInfo HSContainsMI = typeof(HashSet<string>).GetMethod("Contains");
    static Expression subCompile(IEnumerator<string> te, ParameterExpression g) {
        var stack = new Stack<Expression>();

        while (te.MoveNext()) {
            switch (te.Current) {
                case "(":
                    break;
                case ")":
                    goto exit_while;
                case "|":
                case "&":
                    var op = te.Current;
                    var lhs = stack.Pop();
                    var rhs = subCompile(te, g);
                    var opExpr = Expression.MakeBinary(op == "|" ? ExpressionType.OrElse : ExpressionType.AndAlso, lhs, rhs);
                    stack.Push(opExpr);
                    break;
                default:
                    var v = Expression.Constant(te.Current, typeof(string));
                    var test = Expression.Call(g, HSContainsMI, v);
                    stack.Push(test);
                    break;
            }
        }
    exit_while:

        return stack.Pop();
    }

    public static Func<HashSet<string>, bool> Compile(string phrase) {
        var g = Expression.Parameter(typeof(HashSet<string>), "g");

        var te = Tokens(phrase).GetEnumerator();
        var e = subCompile(te, g);

        var ef = Expression.Lambda<Func<HashSet<string>, bool>>(e, g);

        return ef.Compile();
    }
}

给定你的初始值

var A = "ab1, eb4, gr3";

var B = "((ut2 | eg1) | gr3) & ab1";
var C = "ke2 & pa5";

然后你可以将测试值转换为HashSet&lt;string&gt;

var As = A.Split(", ").ToHashSet();

然后你就可以编译测试了:

var fB = PhraseCompiler.Compile(B);
var fC = PhraseCompiler.Compile(C);

并运行测试:

var testBresult = fB(As);
var testCresult = fC(As);

注意:编译器不会进行错误检查,并且可能会针对错误的测试短语抛出异常。

【讨论】:

  • 首先让我非常感谢您!我从中学到了很多。但我认为它实际上没有正确评估。例如A = "ab1, eb4, gr3" 和 B = "(ut2 | eb4) & ab1" 这样 B 应该返回 True,因为 (False OR True) AND True,对吧?另外... var As = A.Split(", ").ToHashSet();它应该是: var As = A.Split(',').ToHashSet();因为 split 只需要一个字符,对吗?
  • 好吧,这是因为 .Split 方法。更改为 ',' 并将所有 " " 替换为 "" 最后得到正确的结果。
  • @fox 鉴于您的输入,它应该可以工作 - 也许您的原始字符串与您的问题不完全一样?
  • 抱歉回复晚了。刚刚发现错误。在我身边。完美运行,非常感谢!多读几遍代码就学到了很多东西!
【解决方案2】:

有效的是:

1) 检查 B 和 C 中的所有值是否存在于 A 中,并将它们替换为“True”或“False”。来自:

B = "(ut2 | eg1) & ab1"

B = "(假|假)&真"

2) 评估这个字符串。

为了评估,我找到了这个方法:https://stackoverflow.com/a/8476664/6552728

    Dim dt As New System.Data.DataTable()
    dt.Columns.Add("", GetType(Boolean))
    dt.Columns(0).Expression = "(False| False) & True"

    Dim r As System.Data.DataRow = dt.NewRow()
    dt.Rows.Add(r)
    Dim result As Boolean = DirectCast(r(0), Boolean)
    MsgBox(result.toString)

这可能不是很好,但它有效。有没有办法使用 LINQ 表达式或二进制表达式评估存储在 String 中的逻辑表达式?我尝试了过去 2 小时,但无法正常工作...

    Dim exp As Expression(Of Func(Of String, Boolean)) = Function(p) p = "True Or False"
    Dim o As Object
    o = Expression.Lambda(exp).Compile().DynamicInvoke()

【讨论】:

  • 要回答您的问题,只能使用 Roslyn 脚本。但是有一个简单的表达式求值器可以用作技巧:new DataTable().Compute("true or false", null) 返回True
猜你喜欢
  • 2010-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多