[英]Is there a built-in IEqualityComparer that compares objects only using their hash value?
[英]IEqualityComparer for Value Objects
我有一个不变的值对象IPathwayModule,其值由以下项定义:
这是我当前的IEqualityComparer实现,似乎可以在一些单元测试中使用。 但是,我认为我不太了解自己做得如何,是否知道自己做得对。 先前的实现有时会在重复的测试运行中失败。
private class StandardPathwayModuleComparer : IEqualityComparer<IPathwayModule>
{
public bool Equals(IPathwayModule x, IPathwayModule y)
{
int hx = GetHashCode(x);
int hy = GetHashCode(y);
return hx == hy;
}
public int GetHashCode(IPathwayModule obj)
{
int h;
if (obj.Class != null)
{
h = obj.Block.GetHashCode() + obj.Module.ModuleId.GetHashCode() + obj.Status.GetHashCode() + obj.Class.ClassId.GetHashCode();
}
else
{
h = obj.Block.GetHashCode() + obj.Module.ModuleId.GetHashCode() + obj.Status.GetHashCode() + "NOCLASS".GetHashCode();
}
return h;
}
}
IPathwayModule绝对是不可变的,具有相同值的不同实例应相等,并产生相同的HashCode,因为它们被用作HashSets中的项目。
我想我的问题是:
不要对Hash函数的结果做等于,它太脆弱了。 而是对每个字段进行字段值比较。 就像是:
return x != null && y != null && x.Name.Equals(y.Name) && x.Type.Equals(y.Type) ...
另外,哈希函数的结果并不真正适合添加。 尝试改用^
运算符。
return obj.Name.GetHashCode() ^ obj.Type.GetHashCode() ...
您不需要在GetHashCode中进行null检查。 如果该值为null,那么您将遇到更大的问题,试图从您无法控制的事物中恢复将毫无用处...
唯一的大问题是平等的实施。 哈希码不是唯一的,对于不同的对象,您可以获得相同的哈希码。 您应该分别比较IPathwayModule的每个字段。
GetHashCode()可以改进一点。 您不需要在int上调用GetHashCode()。 int本身是一个很好的哈希码。 枚举值相同。 您的GetHashCode可以这样实现:
public int GetHashCode(IPathwayModule obj)
{
unchecked {
int h = obj.Block + obj.Module.ModeleId.GetHashCode() + (int) obj.Status;
if (obj.class != null)
h += obj.Class.ClassId.GetHashCode();
return h;
}
}
“ unchecked”块是必需的,因为算术运算中可能会溢出。
您不应该使用GetHashCode()作为比较对象的主要方式。 在现场进行比较。
可能有多个对象具有相同的哈希码(这称为“哈希码冲突”)。
另外,将多个整数值相加时要小心,因为您很容易导致OverflowException。 使用“ exclusive or”(^)组合哈希码或将代码包装到“ unchecked”块中。
您应该实现更好的Equals和GetHashCode版本。
例如,枚举的哈希码就是它们的数值。
换句话说,使用以下两个枚举:
public enum A { x, y, z }
public enum B { k, l, m }
然后在您的实现中,以下值类型:
public struct AB {
public A;
public B;
}
以下两个值将被视为相等:
AB ab1 = new AB { A = A.x, B = B.m };
AB ab2 = new AB { A = A.z, B = B.k };
我假设你不想要那样。
另外,将值类型作为接口传递会将它们装箱,这可能会带来性能问题,尽管可能不多。 您可能会考虑使IEqualityComparer实现直接采用您的值类型。
如果我对您的理解很好,那么您希望听到一些关于您代码的评论。 这是我的话:
GetHashCode
应该一起XOR,而不是添加。 XOR( ^
)可以更好地防止碰撞 GetHashCode
时才这样做。 如果不是,请使用属性及其哈希码并将其组合。 .Class
)却是不可变的 第5点取决于您是否希望在不相同的引用时以值类型引用的对象返回不相等的值。
编辑:您比较许多字符串。 字符串比较在C#中进行了优化。 正如其他人所建议的,您可以在比较中更好地使用==
。 对于GetHashCode,也请使用其他人建议的OR ^
。
感谢所有回应。 我汇总了所有答复者的反馈,并且改进后的IEqualityComparer
现在看起来像:
private class StandardPathwayModuleComparer : IEqualityComparer<IPathwayModule>
{
public bool Equals(IPathwayModule x, IPathwayModule y)
{
if (x == y) return true;
if (x == null || y == null) return false;
if ((x.Class == null) ^ (y.Class == null)) return false;
if (x.Class == null) //and implicitly y.Class == null
{
return x.Block.Equals(y.Block) && x.Status.Equals(y.Status) && x.Module.ModuleId.Equals(y.Module.ModuleId);
}
return x.Block.Equals(y.Block) && x.Status.Equals(y.Status) && x.Module.ModuleId.Equals(y.Module.ModuleId) && x.Class.ClassId.Equals(y.Class.ClassId);
}
public int GetHashCode(IPathwayModule obj)
{
unchecked {
int h = obj.Block ^ obj.Module.ModuleId.GetHashCode() ^ (int) obj.Status;
if (obj.Class != null)
{
h ^= obj.Class.ClassId.GetHashCode();
}
return h;
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.