尝试实现一个GetHashCode覆盖类似于Jon的Skeet的建议,在什么是覆盖System.Object.GetHashCode的最佳算法?我注意到在评估顺序中有一些奇怪的行为,当使用条件运算符对类中的集合属性进行空检查时,会导致语法错误.
考虑以下:
public class Foo { public String Name { get; private set; } public ListBar { get; private set; } public override Int32 GetHashCode() { unchecked { int hash = 17; hash = hash * 23 + this.Name == null ? 0 : this.Name.GetHashCode(); hash = hash * 23 + this.Bar == null ? 0 : this.Bar.GetHashCode(); return hash; } } }
当您收到语法错误时hash = hash * 23 + this.Bar == null ? 0 : this.Bar.GetHashCode();
,此代码将无法编译,特别是指向hash * 23 + this.Bar
段.
错误是
运算符'+'不能应用于'int'和'System.Collections.Generic.List'类型的操作数
你没有得到相同的错误,hash = hash * 23 + this.Name == null ? 0 : this.Name.GetHashCode();
虽然唯一的区别Name
是a string
和Bar
是a List<>
.
将整个条件操作包装在一组括号中会删除错误,但这仍然无法解释为什么集合属性的处理方式与字符串属性不同.
是否有一个原因我不知道这导致对不同类型的操作进行不同的评估?
看看这个功能:
void Crash() { int hash = 123; int crash = hash * 23 + this.Name == null ? 0 : this.Name.GetHashCode(); }
它与你的相似 - 我删除了无法编译的部分.
当Name
设置为null
(演示)时,此功能将以您GetHashCode()
想要的方式崩溃.这看起来很奇怪,因为乍一看检查的性能null
是存在的.
但是,检查它是错误的null
:C#与null
整个表达式进行比较:
hash * 23 + this.Name
这是一个有效的字符串连接.
这提供了一个线索,以解决您的"玄机":这是不是说其实Bar
是一个集合错误导致编译错误,但事实证明Name
是string
被错误地避免编译错误.
区别在于它hash * 23 + this.Name == null
是一个有效的表达式,而hash * 23 + this.Bar == null
不是.+
用于算术和字符串连接,但不用于向列表中添加元素.在这两种情况下,整个hash * 23 + this.... == null
都被视为?:
操作员的条件.