【问题标题】:How to check if two Expression<Func<T, bool>> are the same [duplicate]如何检查两个 Expression<Func<T, bool>> 是否相同[重复]
【发布时间】:2010-10-14 23:09:13
【问题描述】:

是否有可能找出两个表达式是否相同?

如给定以下四个表达式:

        Expression<Func<int, bool>> a = x => false;
        Expression<Func<int, bool>> b = x => false;
        Expression<Func<int, bool>> c = x => true;
        Expression<Func<int, bool>> d = x => x == 5;

那么,至少我们可以看到:

  • a == b
  • a != c
  • a != d

但是我可以做些什么来在我的代码中找到它吗?

看了一下 msdn 库,上面写着

Equals:判断指定的Object是否等于当前的Object。 (继承自Object。)

我猜这意味着至少 Expression 类没有重写 equals 方法以成为 Equatable?那么你会怎么做呢?还是我在这里要求太多了? :p

【问题讨论】:

  • 如果里面有MemberInfo,我的意思是一些方法,字段的属性,那么你可以先获取memberinfo,然后计算它的hash

标签: c# expression equality


【解决方案1】:

您可以查看Linq to db4o 内部使用的类型ExpressionEqualityComparer。它实现了 IEqualityComparer 接口,因此它可用于泛型集合,也可用于独立使用。

它使用ExpressionComparison 类型比较两个表达式是否相等,并使用HashCodeCalculation 从一个表达式计算哈希码。

这都涉及到访问表达式树,所以如果你重复这样做可能会非常昂贵,但它也很方便。

代码可在 GPL 或 dOCL 下获得

例如,这是你的测试:

using System;
using System.Linq.Expressions;

using Db4objects.Db4o.Linq.Expressions;

class Test {

    static void Main ()
    {
        Expression<Func<int, bool>> a = x => false;
        Expression<Func<int, bool>> b = x => false;
        Expression<Func<int, bool>> c = x => true;
        Expression<Func<int, bool>> d = x => x == 5;

        Func<Expression, Expression, bool> eq =
            ExpressionEqualityComparer.Instance.Equals;

        Console.WriteLine (eq (a, b));
        Console.WriteLine (eq (a, c));
        Console.WriteLine (eq (a, d));
    }
}

它确实打印了 True, False, False。

【讨论】:

  • 看起来很有希望,但是这些 ExpressionComparison(a, b).AreEqual 和 HashCodeCalculation(expression).HashCode 是什么?
  • 你可以浏览实现,它们在同一个文件夹中。
  • 我想我会尝试寻找另一种方法来解决我想弄清楚的问题:p 但是您的答案似乎提供了一个可行的解决方案,所以我将其标记为已接受 =)
  • 您必须引入大量代码才能使其正常工作,例如重新实现 ExpressionVisitor :(
  • @JohnNolan,你会的。对于 .net 3.5,ExpressionVisitor 没有公开,所以你必须自己动手。对于 .net 4.0,此代码需要更新为新的表达式类型。由于它不是内置的,因此需要大量工作也就不足为奇了。
【解决方案2】:

作为一个懒惰的答案,您可以检查ToString() - 它至少应该指出它们明显不同的地方(尽管它会在其中包含 var-name,因此必须相同)。

为了准确地检查等价性...更加困难 - 大量工作,涉及许多不同的节点类型。

【讨论】:

  • 大声笑,现在这实际上可能有点工作......:D
  • 不,不是每个表达式都有可用的字符串表示。例如,Convert 并不指示它转换为哪种类型。
  • 完全正确 - 我说它会找到明显错误的答案,但仅此而已。您需要正确地走树,检查实际使用的运算符等,才能完成彻底的工作。
  • 还是谢谢分享。在一些简单的场景中可能会很好。
【解决方案3】:

令我震惊的是,除了最简单的情况外,这可能很难做到。

例如:

var numbers1 = Enumerable.Range(1, 20);
Expression<Func<int, IEnumerable<int>>> a = x => numbers1;
var numbers2 = Enumerable.Range(1, 20);
Expression<Func<int, IEnumerable<int>>> b = x => numbers2;

从技术上讲,它们是相等的,但是如何在不评估每个表达式中返回的 IEnuemrable 的情况下确定呢?

【讨论】:

  • 我在这里问你们:p呵呵。但是,是的,我可以看到问题.. 但我也可以.. 看不到它。因为就像你说的那样,从技术上讲,这些实际上是平等的。而且,至少我会认为,根据节点和数据类型,一棵表达式树应该与另一棵表达式树具有可比性。
  • 在你的例子中,我想我可能认为这些表达式不相等,因为它们包含对不同对象的引用......
  • numbers1 或 numbers2 将被序列化为 ConstantExpression 节点,其值不相等。
  • @Jb: 表达式可以序列化吗?
  • 这些在定义上是不相等的,因为它们都在闭包中引用了不同的 IEnumerable 实例,该闭包变成了一个 ConstantExpression 持有对 Value 属性中 RangeEnumerable 的引用,而那些没有实现既不是 Equals,也不是 GetHashCode。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-17
相关资源
最近更新 更多