【问题标题】:Operator overloading giving error [duplicate]运算符重载给出错误[重复]
【发布时间】:2015-01-02 17:07:33
【问题描述】:

我有一个名为 Point 的类,它重载“==”和“!=”运算符来比较两个 Point 对象。如何将我的 Point 对象与“null”进行比较,这是一个问题,因为当我使用 null 调用 == 或 != 运算符时,在 Equals 方法中会出现问题。请打开一个控制台应用程序,看看我想说什么。我该如何解决它。

public class Point
    {
        public int X { get; set; }
        public int Y { get; set; }


        public static bool operator == (Point p1,Point p2)
        {
            return p1.Equals(p2);
        }

        public static bool operator != (Point p1, Point p2)
        {
            return !p1.Equals(p2);
        }


        public override bool Equals(object obj)
        {
            Point other = obj as Point;

            //problem is here calling != operator and this operator calling  this method again
            if (other != null)
            {
                if (this.X == other.X && this.Y == other.Y)
                {
                    return true;
                }
                return false;
            }

            else
            {
                throw new Exception("Parameter is not a point");
            }
        }
    }

class Program
    {
        static void Main(string[] args)
        {
            Point p1 = new Point { X = 9, Y = 7 };
            Point p2 = new Point { X = 5, Y = 1 };

            p1.X = p2.X;
            p1.Y = p2.Y;

            bool b1=p1==p2;

            Console.ReadKey();
        }
    }

【问题讨论】:

  • 在运算符中检查null,不要抛出。将值与 null 进行比较绝对不是例外情况。
  • 您遇到什么错误?因为在我看来你的代码会导致无限循环?
  • 感谢 Lasse V. Karlsen 解决了这个问题
  • 请原谅这个无耻的插件,但由于这个问题太难了,我将只参考我的existing answer,它显示了在 .NET 中实现相等运算符的 One Right Way™,我将添加你也应该强烈考虑在你的课堂上实现IEquatable interface:这就是它的用途,人们粗鲁地忽略它。 :-( 哦,当覆盖 Equals 时,您必须也覆盖 GetHashCode。否则会出现混乱。

标签: c# .net


【解决方案1】:

您不应在以下位置引发异常:

        if (other != null)
        {
            ...
        }
        else
        {
            throw new Exception("Parameter is not a point");
        }

您可以阅读http://msdn.microsoft.com/en-US/library/336aedhh%28v=vs.85%29.aspx 了解Equals() 是如何正确实现的。

【讨论】:

  • 使用 other!= null 可能是个坏主意,因为您将使用 != 的重载。更好的用户 ReferenceEquals
  • ref == null相比,使用ReferenceEquals(ref, null)并没有真正的优势。恕我直言,第二个更容易阅读。无论如何,我的意图是给出一个答案,为什么 aksers == 运营商失败了。不过,您应该遵守 Microsoft 的指导方针,以确保 Equals 按预期工作。
【解决方案2】:

可能是这样的:

public override bool Equals(object obj)
{
    if (obj != null && obj is Point)
    {
       Point other = (Point)obj;
       if (this.X == other.X && this.Y == other.Y)
       {
           return true;
       }
       return false;
    }
    return false;
}

【讨论】:

    【解决方案3】:

    使用ReferenceEquals 来检查null

    if (ReferenceEquals(other, null))
    

    话虽如此,Equals如果遇到未知的对象类型一般不应该抛出异常,它应该只返回false,因此这是我要写的方法:

    public override bool Equals(object obj)
    {
        Point other = obj as Point;
    
        if (ReferenceEquals(other, null))
            return false;
    
        return (this.X == other.X && this.Y == other.Y);
    }
    

    另一个问题是,如果您将 null 与某物进行比较,您的运算符将引发异常,因为您无法在 null 引用上调用实例方法。

    因此,这是我要编写的完整课程:

    public class Point
    {
        public int X { get; set; }
        public int Y { get; set; }
    
    
        public static bool operator == (Point p1,Point p2)
        {
            if (ReferenceEquals(p1, p2)) return true;
            if (ReferenceEquals(p1, null)) return false;
            return p1.Equals(p2);
        }
    
        public static bool operator != (Point p1, Point p2)
        {
            return !(p1 == p2);
        }
    
        public bool Equals(Point other)
        {
            if (ReferenceEquals(other, null))
                return false;
    
            return (this.X == other.X && this.Y == other.Y);
        }
    
        public override bool Equals(object obj)
        {
            Point other = obj as Point;
            if (ReferenceEquals(other, null))
                return false;
            return Equals(other);
        }
    }
    

    【讨论】:

    • 感谢@Lasse V.Karlsen
    • 但如果参数对象是不同的对象类型,例如 Car,则存在探针。 other.X 导致异常
    • 我也无法将我的 Point 对象与任何地方的 null 进行比较。
    • 不,因为obj as Point 将返回null,然后该方法将返回false。它永远不会尝试比较Car 中的XY ,因为它不是Point
    • 我不明白您所说的“我也无法将我的 Point 对象与任何地方的 null 进行比较。”。
    【解决方案4】:

    在 C# 中处理相等性可能很棘手,而且很容易像您在这里遇到的那样陷入陷阱。

    在处理相等时,我总是遵循相同的模式,无论是在值类型中还是在引用类型中(null 显然检查除外):实现一个静态私有帮助器方法来处理所有可能性,并让所有相等检查调用此方法。

    所以,在你的情况下,我会做以下事情:

    更新:修正了一个错字,equals 应该是 private,而不是 public

     private static bool equals(Point p1, Point p2)
     {
         if (object.ReferenceEquals(p1, p2))
             return true;
    
         if (object.ReferenceEquals(p1, null) || object.ReferenceEquals(p2, null))
             return false;
    
         return p1.X == p2.X && p1.Y == p2.Y;
     }
    

    好的,那我们在这里做什么?

    1. 我们首先检查两个对象是否具有相同的引用。如果 就是这样,那么它们必须相等,因为它们是相同的 目的。请注意,这会处理 null == null 的情况。

    2. 然后我们检查两个参数中是否有任何一个等于null。 如果是,那么我们知道p1p2 不相等。

      要查看参数是否等于null,我们使用 satic 方法 bool object.ReferenceEquals(,) 避免您遇到的问题 这再次调用了您的平等实现。

    3. 最后,我们现在知道我们有两个非空 Point 参数,所以我们开始 先实现两个Point对象的具体相等逻辑。

    通过这个简单的方法,我们处理了类中所有可能的相等检查。现在我们只需从所有可能的相等方法和运算符中调用此方法:

    public static bool operator ==(Point p1, Point p2)
    {
        return Point.equals(p1, p2);
    }
    
    public static bool operator !=(Point p1, Point p2)
    {
        return !Point.equals(p1, p2);
    }
    
    public override bool Equals(object obj)
    {
        return Point.equals(this, obj as Point);
    }
    

    此外,您应该实现IEquatable<Point> 接口,该接口非常简单。这在处理 值类型 时特别重要,因为您在执行相等检查时避免了装箱转换:

    public bool Equals(Point p)
    {
        return Point.equals(this, p);
    }
    

    最后但同样重要的是,您将覆盖 Equals 方法以及 ==!= 运算符,因此您还应该以与相等实现一致的方式覆盖 int GetHashCode():如果 p1 == p2 然后 @ 987654343@必须等于p1.GetHashCode()(注意,这并不意味着如果p1 != p2p1.GetHashCode()不能等于p2.GetHashCode()):

    public override int GetHashCode()
    {
        return this.X ^ this.Y;
    }
    

    希望这个小指南有所帮助。

    【讨论】:

    • 您的解决方案非常好,谢谢。
    • 这种方法的问题在于它以Equals(Object, Object)(实际上,令人困惑的是equals)的形式实现了Equals(Object),它被覆盖了。但是 .NET 中的 Object 类被设计为完全相反地处理这个问题:也就是说,预定义的静态 Object.Equals(Object, Object) 方法是根据 Object.Equals(Object) 实现的,并且在后者被覆盖时工作得很好.这就是实现者应该采取的路线。这样,它们就少了一种方法。
    • @KonradRudolph 我在哪里做了不同的事情?我的静态equals(Point, Point) 是我班级的私有实现细节;我看不出object.Equals(object, object) 的问题在哪里。可能我误会你了。
    • @KonradRudolph 哎呀,现在我明白你在说什么了。那是一个错字! equals 应该是私有的。感谢您指出。
    • @InBetween 实际上,我主要是被equalsEquals 混淆了。但是,您的代码仍然有一个小缺陷,即性能错误:您的equals 测试第一个参数是否为null。但是,Point.Equals(Object) 已经这样做了。含蓄地。因此,该测试是多余的,但您的代码仍然需要它用于不同的代码路径。这并不理想。当然,这可以说是可以接受的,但我个人会避免在核心功能中出现这种冗余:在热代码路径中可能会大量使用相等比较。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-14
    • 2016-10-18
    • 1970-01-01
    • 1970-01-01
    • 2012-03-25
    • 1970-01-01
    相关资源
    最近更新 更多