在重载操作符函数中无法调用“等于”?

时间:2017-08-05 05:48:54

标签: c# .net

我发现了一个有趣的事情,把它作为C#程序:

namespace ConsoleApplication1
{
    using System;

    public class Student
    {
        public int ID { get; set; }
        public string Name { get; set; }

        public override bool Equals(object comparedStudent)
        {
            Student stu = comparedStudent as Student;

            if (stu == null)
            {
                return false;
            }
            return ID.Equals(stu.ID) && Name.Equals(stu.Name);
        }

        public override int GetHashCode()
        {
            return ID.GetHashCode() ^ Name.GetHashCode();
        }
        /// <summary>
        /// Notice that this will cause NullPointException……Why?
        /// If I use "return s1.ID==s2.ID && s1.Name==s2.Name", that'd be OK.
        /// </summary>
        public static bool operator ==(Student s1, Student s2)
        {
            return s1.Equals(s2);
        }

        public static bool operator !=(Student s1, Student s2)
        {
            return !s1.Equals(s2);
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            Student s1 = new Student();
            s1.ID = 1;
            s1.Name = "YourName";
            Student s2 = new Student();
            s2.ID = 1;
            s2.Name = "YourName";

            //Why there's an exception here (NullPoint exception)
            bool isSame = (s1 == s2);
            Console.WriteLine(isSame);
        }
    }
}

如果您复制我的代码并运行,则会抛出NullPointer异常。为什么呢?

PS:如果我在重载运算符+中使用“return s1.ID == s2.ID&amp;&amp; s1.Name == s2.Name”,那就没问题了。

为什么呢?我正在使用net4.0 VS2015。

如果通过将调试点放在“bool isSame =(s1 == s2);”进行调试您很快就会发现它进入“==”重载函数3次,最后,s1和s2为空!

1 个答案:

答案 0 :(得分:2)

虽然Ians评论将解决上述问题,但它不会最终解决问题。 问题的根源在于比较运算符的过载。

程序员希望null == myobject以及myobject == null成为有效的表达式(正如您在.Equals的实现中所做的那样)。由于在s1.Equals中调用了成员函数operator ==,因此对上述类不起作用。

因此,比较运算符的任何合理重载都应该能够处理真正的任何输入而不会抛出异常。有疑问只是回归虚假。 这包括空值以及尚未分配所有属性的类实例。

public static bool operator ==(Student s1, Student s2) =>
  ReferenceEquals(s1, s2) || (!ReferenceEquals(s1, null) && s1.Equals(s2));

理由:

  • ReferenceEquals(s1, s2)处理null == null以及x == x。后者不需要比较内容。
  • s1 != null处理null == x
  • x == null已由.Equals实施处理。

顺便说一句:建议在通过实现.Equals重载相等运算符时提供强类型IEquatable<Student>实现。

此外,您不应该使用.Equals来比较组件,因为如果到目前为止尚未分配Name,原因可能会因类似原因而失败。

public override bool Equals(object obj) =>
  Equals(obj as Student);

public bool Equals(Student stu) =>
  !ReferenceEquals(stu, null) && ID == stu.ID && Name == stu.Name;

另见Yuval Itzchakov对问题Operator overloading ==, !=, Equals

的回复