问题 Linq表达式如何确定平等?


我正在考虑使用Linq Expression作为字典中的键。但是,我担心我会得到奇怪的结果,因为我不知道Linq表达式如何确定Equality。

派生自Expression的类是否比较值相等或引用相等?或者换句话说,

        Expression<Func<object>> first = () => new object(); 
        Expression<Func<object>> second = ()=>new object();
        bool AreTheyEqual = first == second;

6097
2018-02-17 18:14


起源

你试过吗? LinqPad 非常适合测试代码的小片段。 - R. Martinho Fernandes
问题不在于正确的前提;一个 Dictionary<,> 不 不 使用 == 密钥相等的运算符。 - Ani
在这种情况下,它们既没有相同的引用也没有值。 - Rangoric
@Rangoric在我看来他们的价值是相同的(即使他们使用引用相等语义):生成的表达式树将具有相同类型和相同值的对象。甚至他们的字符串表示也是一样的。 - R. Martinho Fernandes


答案:


你的测试比较 表达式。表达本身只提供参考平等;你的测试可能会显示“假”。为了脸颊 语义 你需要做大量工作的平等 - 例如:

x => 123

y => 123

当量?作为原始测试,您可以比较ToString(),但这将非常脆弱。


11
2018-02-17 18:22



不会.ExitationExpression的名称会有所不同。但我得到你的漂移。我认为行为应该是我的表达式返回true并且表达式将返回false,因为实现可能取决于参数的名称。 - smartcaveman
@smartcaveman甚至允许像这样的“无辜”变化,我认为在一般情况下这样做是一个真正的斗争。 - Marc Gravell♦
@smartcaveman:如果你不想像Marc所展示的语义相同,并希望一切都完全相同,包括参数名称等,你可以编写自己的ExpressionVisitor进行比较。 - R. Martinho Fernandes
@Marc,是的,我同意你的意见。我只是希望它已经在我身上烘烤了。 - smartcaveman
@Martinho @smartcaveman - 确实;一个完整的访问者是很多工作,尤其是在4.0 - Marc Gravell♦


比较任何非值类型的对象(包括表达式)与==比较对象引用,因此它们将不相等。然而,正如一位评论者指出的那样,字典会被使用 Equals 和 GetHashCode 确定平等,这仍然默认最终确定它们不相等。

您可以创建一个继承的类 System.Linq.Expression 和覆盖 GetHashCode 和 Equals 但是,以某种方式使用结果,并将其用作词典的关键字。


0
2018-02-17 18:25



你的第一个陈述是误导性的。类型可以提供static ==运算符 - Marc Gravell♦
此外,还有无数的Expression类;子类路由不是这里的选项。 - Marc Gravell♦
那个更好吗?或者你指的是我不明白的东西? - Jamie Treworgy
所有对象都不是这样。 (考虑字符串)。我不认为overriding会获得我想要的功能,但我可以创建一个评估表达式的包装器,但正如Marc所说,这可能会导致更多的工作而不是值。 - smartcaveman
@jamietre不,甚至一个类都可以定义一个==运算符。如果是,编译器将使用定义的运算符而不是referenceequals。 - Marc Gravell♦


正如其他人所指出的,Expression的==运算符使用默认的“引用相等”检查 - “它们是否都是对堆中相同位置的引用?”。这意味着像您的示例之类的代码可能会返回false,因为您的表达式文本将被实例化为不同的Expression实例,而不管语义是否相等。使用lambdas作为事件处理程序有类似的挫败感:

MyEvent += (s, a) => DoSomething();
...
MyEvent -= (s, a) => DoSomething(); //<-- will NOT remove the added handler

检查语义相等是很棘手的。在这种特殊情况下,您可以访问表达式树的所有节点,并比较所有字符串,值类型和方法引用,以确定它们执行相同的操作。但是,通过检查,以下示例中的两个lambdas在语义上是等效的,但是您很难编写一个方法来证明它:

   public void MyMethod() {...}
   public void AnotherMethod { MyMethod(); };

   ...

   Action one = () => MyMethod();
   Action two = () => AnotherMethod();

   var equal = one == two; // false

0
2018-02-17 18:39



我认为这两种方法不应该相等。但是,您知道检查方法体的方法吗? - smartcaveman
您可以通过获取相关方法的MethodInfo来反射性地获取方法体。可能还有一种方法可以使用PartialEvaluator(来自IQToolkit)来检查它;我知道Linq2SQL和其他一些与Linq提供商的ORM似乎能够转换用户定义的扩展方法,我只是不知道复制它有多容易(可能根本不容易)。 - KeithS