繁体   English   中英

C#重载运算符==

[英]C# Overloading operator ==

//overloading operator ==
class Point
{
    private int m_X,int m_Y;

    public static operator == (Point p1 ,Point p2)
    {
        if(object.ReferenceEquals(p1,p2)
            return true;

        if((object)(p1) == null) || ((object)(p2) ==null)
            return false;

        return( (p1.x == p2.x) && (p1.x == p2.x));
    }

    //overloading the != operator
}
  1. 是否有必要覆盖Equals功能
  2. 如果p1和p2没有被类型转换为对象,则抛出堆栈溢出异常为什么我们需要将Point对象强制转换为对象。
  1. 如果重载==运算符,通常优先覆盖Equals()方法,因为它们应返回相同的结果,如果不覆盖它,Object.Equals()将推迟到ReferenceEquals(),因此运算符和方法将有不同的结果。 这样做的简单方法是让操作符调用Equals()方法(它应该具有与此处类似的代码)

  2. 您必须强制转换为Object来执行== null比较,因为您处于重载的==方法中,因此如果没有强制转换为基类型,您的运算符方法会无休止地调用它来尝试评估p1 == null

Overriding Equals()和operator ==的指南中 ,它说:

operator ==的重载中的常见错误是使用(a == b),(a == null)或(b == null)来检查引用相等性。 这反而导致调用重载的运算符==,导致无限循环。 使用ReferenceEquals或将类型转换为Object,以避免循环。

1)重载.Equals()并不是绝对必要的,但这被认为是良好的编码实践。 您通常不希望.Equals()进行参考比较,运算符==进行值比较。 您还应该重写GetHashCode和operator!=。

2)发生堆栈溢出是因为你一遍又一遍地调用自己的运算符==。 当转换不存在时,编译器有两个选择:

  1. 使用你的operator ==(Point,Point)的实现

  2. 使用Object的运算符==(对象,对象)

因为#1更具体,所以编译器会选择您的实现。 对对象进行Explicilty强制转换可确保调用Object的operator ==实现。 您可以使用以下代码在没有强制转换的情况下实现相同的目标:

if (Object.Equals(p1, null) || Object.Equals(p2, null))

1 - 没有必要,尽管它会很好。 实际上,您尝试在运算符中实现的行为通常放在Equals方法上。

2 - 如果不进行强制转换,p1 == null将调用运算符本身,从而导致堆栈溢出。

此链接指向重写等于和==的MS准则。

回答你的第一个问题:这在技术上并不是强制性的,但是如果你不重写Equals ,它将执行==运算符最初做的事情:返回System.Object版本,它检查引用是否相同。 因此,如果你想让Equals做与==相同的事情,你也必须覆盖它。 看看“ 我什么时候应该使用==以及何时应该使用Equals? ”从那篇文章中:

对于尚未重载==的引用类型,它会比较两个引用是否引用同一个对象 - 这正是Equals在System.Object中的实现。

第二个问题的答案是,如果你不将它们强制转换回object ,当你评估p1 == null时,会对你的==运算符进行无限递归调用。 看见? 你将在内部调用== ,因为p1是一个Point ,这是Point类的==运算符。 相反,您要在该行上执行的操作是确定两个引用是否为null,运算符的System.Object版本将执行此操作。

堆栈溢出异常可能是因为在p1上使用==再次无休止地调用你的==运算符。

在这种情况下覆盖==运算符是个好主意。

您将受益于进一步研究==.Equals()之间的差异以及Value和Reference类型之间的区别( Point是一个struct ,意味着它是一个Value类型)。

您当前的覆盖基本上结合了引用和值相等性检查 - 使其结合了==.Equals()的行为。

关于你的第二个问题,你的==重载调用自身(在重载中使用==运算符)。 要考虑的另一个方面是Point是一个值类型,因此永远不能为null (除非它包含在Nullable<Point> )。

我同意覆盖==可能是要走的路。 但是你可以通过使它成为struct而不是class来清理大量的代码。 你不必担心空检查,它会更符合逻辑,因为它确实是一个“价值”。

正如其他人所说的那样,如果你创建一个==运算符,则不必严格覆盖.Equals(object o) ,但它是首选的。 此外,如果不这样做,编译器将生成一些警告。

warning CS0660: '<Type>' defines operator == or operator != but does not override Object.Equals(object o)
warning CS0661: '<Type>' defines operator == or operator != but does not override Object.GetHashCode()

因此,即使编译器指向您重写.Equals(Object o)并且== defer to that。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM