Expression.Equal 支持 "Equals" 重载和隐式运算符的解决方案

Expression.Equal solution that honors "Equals" overloads and implicit operators

在运行时使用表达式树编译代码时,我们可能会发现自己需要检查不确定类型的对象是否相等。

如果我们简单地为每种情况手动编写代码,编译器会为我们考虑很多事情。想到最多的是:

  1. 如果通用重载 T1.Equals<T2>T2.Equals<T1> 可用,则使用它。
  2. 否则,如果任一类型都有一个隐式运算符让我们应用 (1),则使用它。
  3. 否则,使用 bool Equals(object)(当然考虑到潜在的覆盖)。

遗憾的是,Expression.Equal(Expression left, Expression right)并没有为我们做所有这些事情。

我们如何才能实现编译器通常提供的相同行为?

假设我们只比较相同编译时类型的对象,我们可以在构建调用表达式时遵循一个简单的两步过程:

  1. 如果编译时类型实现 IEquatable<T>,其中 T 是类型本身,则调用相应的 Equals(T) 方法。

这可以防止对实现 IEquatable<T> 的结构进行装箱,正是出于这个原因,他们经常这样做。它具有在 classes 上调用最明显的 Equals 重载的额外好处。

  1. 否则,调用常规(可能被覆盖)Equals(object)方法。

有两个注意事项。

第一个警告是 Equals(T) 方法在没有 IEquatable<T> 接口的情况下被忽略。这可能被认为是合理的:如果开发人员没有添加接口,他们是否希望该方法用于此目的并不完全清楚。

第二个警告是,对于 classes,base class 上的 IEquatable<T> 接口将被忽略,而编译器可能更喜欢与之对应的过载。幸运的是,我们可以合理地期望 Equals(object) 到 return 与 T 对象的 Equals(T) 相同的结果。如果不是,则开发人员提供了一个模棱两可的相等定义,并且所有赌注都关闭了。