Linq 表达式如何确定相等性?
Posted
技术标签:
【中文标题】Linq 表达式如何确定相等性?【英文标题】:How do Linq Expressions determine equality? 【发布时间】:2011-06-29 07:20:19 【问题描述】:我正在考虑使用 Linq 表达式作为字典中的键。但是,我担心我会得到奇怪的结果,因为我不知道 Linq 表达式如何确定 Equality。
从表达式派生的类是比较值相等还是引用相等?或者换句话说,
Expression<Func<object>> first = () => new object();
Expression<Func<object>> second = ()=>new object();
bool AreTheyEqual = first == second;
【问题讨论】:
你试过了吗? LinqPad 非常适合测试小型 sn-ps 代码。 这个问题不是基于正确的前提;Dictionary<,>
确实不使用==
运算符进行键相等。
在这种情况下,它们既没有相同的参考也没有价值。
@Rangoric 在我看来它们的值是相同的(即使它们使用引用相等语义):生成的表达式树将具有具有相同类型和相同值的对象。甚至它们的字符串表示形式也是一样的。
【参考方案1】:
您的测试比较表达式。表达式本身只提供引用相等;您的测试可能会显示“假”。为了实现语义平等,你需要做很多工作,例如 - 是:
x => 123
和
y => 123
等价的?作为一个粗略的测试,您可以比较 ToString(),但这会非常脆弱。
【讨论】:
没有。 ParameterExpression 的名称会有所不同。但我明白你的意思。我认为行为应该是我的表达式返回 true 而你的表达式返回 false,因为实现可能取决于参数的名称。 @smartcaveman 甚至允许像这样的“无辜”更改,我认为在一般情况下这样做会很困难。 @smartcaveman:如果你不想像 Marc 展示的那样语义相等,并且希望一切都完全相等,包括参数名称等,你可以编写自己的 ExpressionVisitor 进行比较。 @Marc,是的,我同意你的看法。我只是有点希望它已经为我烤好了。 @Martinho @smartcaveman - 确实;一个完整的访问者需要做很多工作,尤其是在 4.0【参考方案2】:用 == 比较任何两个不是值类型(包括表达式)的对象会比较对象引用,因此它们不会相等。不过,正如评论者指出的那样,字典将使用 Equals
和 GetHashCode
来确定相等性,默认情况下仍会最终确定它们不相等。
您可以创建一个继承 System.Linq.Expression
的类并覆盖 GetHashCode
和 Equals
以以某种方式使用结果,并将其用作字典的键。
【讨论】:
您的第一句话具有误导性。类型可以提供静态 == 运算符 此外,还有无数的 Expression 类;子类路由在这里不是一个选项。 这样更好吗?还是你指的是我不明白的东西? 并非所有对象都如此。 (考虑字符串)。我认为覆盖不会获得我想要的功能,但我可以创建一个评估表达式的包装器,但正如 Marc 指出的那样,这可能会导致工作量多于价值。 @jamietre 不,即使是一个类也可以定义一个 == 运算符。如果是这样,编译器将使用定义的运算符而不是 referenceequals。【参考方案3】:正如其他人所指出的,Expression 的 == 运算符使用默认的“引用相等”检查 - “它们都是对堆中同一位置的引用吗?”。这意味着像您的示例这样的代码可能会返回 false,因为您的表达式文字将被实例化为不同的 Expression 实例,而不管语义是否相等。使用 lambdas 作为事件处理程序也有类似的挫败感:
MyEvent += (s, a) => DoSomething();
...
MyEvent -= (s, a) => DoSomething(); //<-- will NOT remove the added handler
检查语义相等性很棘手。在这种特殊情况下,您可能能够访问表达式树的所有节点并比较所有字符串、值类型和方法引用,以确定它们是否执行相同的操作。但是,通过检查,以下示例中的两个 lambda 在语义上是等效的,但是您很难编写方法来证明它:
public void MyMethod() ...
public void AnotherMethod MyMethod(); ;
...
Action one = () => MyMethod();
Action two = () => AnotherMethod();
var equal = one == two; // false
【讨论】:
我认为这两种方法不应该评估为相等。但是,你知道检查方法体的方法吗? 您可以通过获取相关方法的 MethodInfo 来反射性地获取方法主体。也可能有一种方法可以使用 PartialEvaluator(来自 IQToolkit)来检查它;我知道 Linq2SQL 和其他一些带有 Linq 提供程序的 ORM 似乎能够转换用户定义的扩展方法,我只是不知道复制它有多容易(可能根本不容易)。以上是关于Linq 表达式如何确定相等性?的主要内容,如果未能解决你的问题,请参考以下文章