Expression.Equal 支持 "Equals" 重载和隐式运算符的解决方案
Expression.Equal solution that honors "Equals" overloads and implicit operators
在运行时使用表达式树编译代码时,我们可能会发现自己需要检查不确定类型的对象是否相等。
如果我们简单地为每种情况手动编写代码,编译器会为我们考虑很多事情。想到最多的是:
- 如果通用重载
T1.Equals<T2>
或 T2.Equals<T1>
可用,则使用它。
- 否则,如果任一类型都有一个隐式运算符让我们应用 (1),则使用它。
- 否则,使用
bool Equals(object)
(当然考虑到潜在的覆盖)。
遗憾的是,Expression.Equal(Expression left, Expression right)
并没有为我们做所有这些事情。
我们如何才能实现编译器通常提供的相同行为?
假设我们只比较相同编译时类型的对象,我们可以在构建调用表达式时遵循一个简单的两步过程:
- 如果编译时类型实现
IEquatable<T>
,其中 T
是类型本身,则调用相应的 Equals(T)
方法。
这可以防止对实现 IEquatable<T>
的结构进行装箱,正是出于这个原因,他们经常这样做。它具有在 classes 上调用最明显的 Equals
重载的额外好处。
- 否则,调用常规(可能被覆盖)
Equals(object)
方法。
有两个注意事项。
第一个警告是 Equals(T)
方法在没有 IEquatable<T>
接口的情况下被忽略。这可能被认为是合理的:如果开发人员没有添加接口,他们是否希望该方法用于此目的并不完全清楚。
第二个警告是,对于 classes,base class 上的 IEquatable<T>
接口将被忽略,而编译器可能更喜欢与之对应的过载。幸运的是,我们可以合理地期望 Equals(object)
到 return 与 T
对象的 Equals(T)
相同的结果。如果不是,则开发人员提供了一个模棱两可的相等定义,并且所有赌注都关闭了。
在运行时使用表达式树编译代码时,我们可能会发现自己需要检查不确定类型的对象是否相等。
如果我们简单地为每种情况手动编写代码,编译器会为我们考虑很多事情。想到最多的是:
- 如果通用重载
T1.Equals<T2>
或T2.Equals<T1>
可用,则使用它。 - 否则,如果任一类型都有一个隐式运算符让我们应用 (1),则使用它。
- 否则,使用
bool Equals(object)
(当然考虑到潜在的覆盖)。
遗憾的是,Expression.Equal(Expression left, Expression right)
并没有为我们做所有这些事情。
我们如何才能实现编译器通常提供的相同行为?
假设我们只比较相同编译时类型的对象,我们可以在构建调用表达式时遵循一个简单的两步过程:
- 如果编译时类型实现
IEquatable<T>
,其中T
是类型本身,则调用相应的Equals(T)
方法。
这可以防止对实现 IEquatable<T>
的结构进行装箱,正是出于这个原因,他们经常这样做。它具有在 classes 上调用最明显的 Equals
重载的额外好处。
- 否则,调用常规(可能被覆盖)
Equals(object)
方法。
有两个注意事项。
第一个警告是 Equals(T)
方法在没有 IEquatable<T>
接口的情况下被忽略。这可能被认为是合理的:如果开发人员没有添加接口,他们是否希望该方法用于此目的并不完全清楚。
第二个警告是,对于 classes,base class 上的 IEquatable<T>
接口将被忽略,而编译器可能更喜欢与之对应的过载。幸运的是,我们可以合理地期望 Equals(object)
到 return 与 T
对象的 Equals(T)
相同的结果。如果不是,则开发人员提供了一个模棱两可的相等定义,并且所有赌注都关闭了。