在结构中需要覆盖哪些内容才能确保相等操作正确?

正如标题所说: 我需要覆盖 ==操作符吗? .Equals()方法怎么样? 我遗漏了什么吗?

44802 次浏览

来自 msdn 的示例

public struct Complex
{
double re, im;
public override bool Equals(Object obj)
{
return obj is Complex c && this == c;
}
public override int GetHashCode()
{
return re.GetHashCode() ^ im.GetHashCode();
}
public static bool operator ==(Complex x, Complex y)
{
return x.re == y.re && x.im == y.im;
}
public static bool operator !=(Complex x, Complex y)
{
return !(x == y);
}
}

两者之间的基本区别在于,==操作符是静态的,也就是说,要调用的适当方法是在编译时确定的,而 Equals方法是在实例上动态调用的。
定义这两个结构可能是最好的办法,即使对于结构来说这没有那么重要,因为结构不能被扩展(一个结构不能从另一个结构继承)。

你也应该实现 IEqutable < T > ,这里是框架设计指南的摘录:

在值类型上实现 IEqutable。 值类型上的 Object.Equals 方法导致装箱,而其 默认实现的效率不是很高,因为它使用了反射。 平等可以提供更好的性能 实现,以便它不会导致装箱。

public struct Int32 : IEquatable<Int32> {
public bool Equals(Int32 other){ ... }
}

请遵循与... ... 相同的指导方针 重写对象。等于当 实现了等式,等于。 有关详细信息,请参阅8.7.1节 关于重写对象的指导原则

不幸的是,我没有足够的声誉来评论其他条目。因此,我张贴可能的增强顶级解决方案在这里。

如果我错了,请纠正我,但上面提到的实现

public struct Complex
{
double re, im;
public override bool Equals(Object obj)
{
return obj is Complex && this == (Complex)obj;
}
public override int GetHashCode()
{
return re.GetHashCode() ^ im.GetHashCode();
}
public static bool operator ==(Complex x, Complex y)
{
return x.re == y.re && x.im == y.im;
}
public static bool operator !=(Complex x, Complex y)
{
return !(x == y);
}
}

有重大缺陷,我指的是

  public override int GetHashCode()
{
return re.GetHashCode() ^ im.GetHashCode();
}

XORing 是对称的,所以复杂(2,1)和复杂(1,2)会产生相同的 hashCode。

我们也许应该做些类似的东西:

  public override int GetHashCode()
{
return re.GetHashCode() * 17 ^ im.GetHashCode();
}

为了完整起见,我还建议重载 Equals方法:

public bool Equals(Complex other)
{
return other.re == re && other.im == im;
}

这是一个真正的速度改进,因为 Equals(Object obj)方法的输入参数没有装箱

使用值类型的一些最佳实践:

  • 让它们永恒不变
  • 重写 Equals (将对象作为参数的参数) ;
  • 重载 Equals 获取同一值类型的另一个实例(例如 * Equals (Complexother)) ;
  • 重载运算符 = = 和! = ;
  • 覆盖 GetHashCode

这来自这篇文章: http://theburningmonk.com/2015/07/beware-of-implicit-boxing-of-value-types/

大多数情况下,您可以避免使用 structs 实现 Equals 和 GetHashcode-因为编译器会自动实现 Value 类型,对引用成员使用按位内容 + 反射。

看看这个帖子: 对于数据存储结构/类,哪种方式最好?

因此,为了方便使用,您仍然可以实现 = = 和! = 。

但是在大多数情况下,您可以避免实现 Equals 和 GetHashcode。
必须实现 Equals 和 GetHashCode 的情况是针对一个不希望考虑的字段。
例如,一个字段随着时间的推移而变化,比如“人的年龄”或“汽车的速度”(如果你想在同一个地方在字典里找到它,那么物体的标识就不应该改变)

问候,最好的暗号