关于重载运算符的小问题!

时间:2021-11-07 14:17:45
class Person {
        private string _name;   //姓名.
        private string _id;

        public string Id {
            get { return _id; }
            set { _id = value; }
        }
        public string Name {
            get { return _name; }
            set { _name = value; }
        }

        //override the "==" operator.
        //It does not need the implicit(For it just Compare Two stuff rather than Conversion).
        public static bool operator ==(Person p1, Person p2) {
            if(p1 == null || p2 == null)
                throw new NullReferenceException("Person is Null!");
            return (p1.Name == p2.Name && p1.Id == p2.Id);
        }

        public static bool operator !=(Person p1, Person p2) {
            return !(p1 == p2);
        }

        //Use the tools to override.
        public override bool Equals(object obj) {
            return base.Equals(obj);
        }

        //Use the tools to override.
        public override int GetHashCode() {
            return base.GetHashCode();
        }
    }


有点不明白的是,当我重写运算符时(==,!=).编译器要我还要override Object的 Equals()方法和 HashCode()方法.
为什么哈?还有,该怎么重写哈?

11 个解决方案

#1


那个只是警告,没有非要让你重写Equals。既然是只是警告,就不一定靠谱,你不一定要把警告当作问题。

这里就是的。你应该调用 Equals来进行判断 if (p1.Equals(null) || p2.Equals(null)),不应该在 == 重载方法中直接使用 == 运算符。这里的警告只是体现这个建议而已,你不要对编译器警告简单地都当作什么问题对待。

#2


对于编译器警告,不要夸张它。你应该自己测试。

#3


引用 2 楼 sp1234 的回复:
对于编译器警告,不要夸张它。你应该自己测试。


你的意思是,应该这样重写是么?
public static bool operator ==(Person p1, Person p2) {
            //if(p1 == null || p2 == null)
            if(ReferenceEquals(p1, p2))
            throw new NullReferenceException("Person is Null!");
            return (p1.Name == p2.Name && p1.Id == p2.Id);
        }


不是,我没有当它很夸张,只是不理解为什么要重写而已,也就是说,我不知道为什么编译器要提示重写!
sp1234
我查一下资料,说如果 override "==","!=".
编译器会警告,要重写Object 的 Equals() 方法和 HashCode方法.
它解释说: 原因是 Equals 方法应实现与 "=="运算符相同类型的相等逻辑.

这是什么意思哈...帮我解释一下...

#4


不需要重写,只不过你的代码要改为  if (p1.Equals(null) || p2.Equals(null))。

这个编译器警告不靠谱,否则它也不会仅仅是警告了。

因为你在 == 重载中又使用了它自身,所以就算是最傻的程序员如果死记硬背某个条文之后也会说出跟这个编译器警告相同的梦话来。所以说这个警告只不过是另外一个意思,不要死抠它的字眼。

#5


其实我在 #1 楼帮你解释清楚了,你又要求我再次解释一遍。

#6


当你在 == 重载中使用 Equals,你就明白其意思了。假设你的程序根本不会产生死循环,那么你根本无须理会这个编译警告。但是假设有可能产生死循环,那么你可以重新定义你的 Equals 方法,但是其实根本原因还是你在 == 中存在的(可能存在)的死循环。所以这个编译器警告根本是模棱两可的,你用测试就能证实根本不是“需要重载 Equals”的问题,而是编译器建议你要注意别在 == 中写出死循环代码。

#7


==和object.Equals都是用来比较对象的相等性的,它们返回不同的结果会让用户感到困惑。一般来说,它们的行为应该一致[1],所以编译器会提醒你在重载了operator_equality之后要重写object.Equals.
比如你看string,它重载了==运算符,同时就也重写了object.Equals。

除了==和object.Equals,如果你的类型实现了IEquatable<T>,它也应该使用相同的比较逻辑。

如果不这样的话,那就乱套了,三种用于比较类型相等性的API返回三种不同的结果。

至于GetHashCode,由于它是用来在hash表中分组的,所以对于“相同”的对象,GetHashCode应该返回相同的值。你改变了==和object.Equals的行为,那么GetHashCode的行为也应该改变,否则就不能满足这一要求。所以当你你重写了object.Euqals时编译器会提醒你重写GetHashCode。


[1]前面说==和object.Equals应该保持行为一致,有一个例外。
因为C#没有提供判断引用相等的语法,大部分程序员都习惯了使用==而不是Object.ReferenceEquals来判断引用相等,所以C#中的引用类型最好不要重载==,而是要保持它的默认行为。也就是说object.Equals,IEquatable<T>.Equals使用你的自定义比较逻辑,而==保留默认的对象引用比较。

但是反过来,如果你打破这条建议,重载了引用类型的==运算符,那么object.Equals和IEquatable<T>.Equals应该使用和他相同的逻辑。

#8


引用 7 楼 hhddzz 的回复:
==和object.Equals都是用来比较对象的相等性的,它们返回不同的结果会让用户感到困惑。一般来说,它们的行为应该一致[1],所以编译器会提醒你在重载了operator_equality之后要重写object.Equals.
比如你看string,它重载了==运算符,同时就也重写了object.Equals。

除了==和object.Equals,如果你的类型实现了IEquatable<T>,它也应该使用相同的比较逻辑。

如果不这样的话,那就乱套了,三种用于比较类型相等性的API返回三种不同的结果。

至于GetHashCode,由于它是用来在hash表中分组的,所以对于“相同”的对象,GetHashCode应该返回相同的值。你改变了==和object.Equals的行为,那么GetHashCode的行为也应该改变,否则就不能满足这一要求。所以当你你重写了object.Euqals时编译器会提醒你重写GetHashCode。


[1]前面说==和object.Equals应该保持行为一致,有一个例外。
因为C#没有提供判断引用相等的语法,大部分程序员都习惯了使用==而不是Object.ReferenceEquals来判断引用相等,所以C#中的引用类型最好不要重载==,而是要保持它的默认行为。也就是说object.Equals,IEquatable<T>.Equals使用你的自定义比较逻辑,而==保留默认的对象引用比较。

但是反过来,如果你打破这条建议,重载了引用类型的==运算符,那么object.Equals和IEquatable<T>.Equals应该使用和他相同的逻辑。

嗯....你和 6 楼说的都很好了...谢谢...

#9


引用 6 楼 sp1234 的回复:
当你在 == 重载中使用 Equals,你就明白其意思了。假设你的程序根本不会产生死循环,那么你根本无须理会这个编译警告。但是假设有可能产生死循环,那么你可以重新定义你的 Equals 方法,但是其实根本原因还是你在 == 中存在的(可能存在)的死循环。所以这个编译器警告根本是模棱两可的,你用测试就能证实根本不是“需要重载 Equals”的问题,而是编译器建议你要注意别在 == 中写出死循环代码。


知道了...就是防止 递归调用...你说的好多...但是....现在才说到重点哦...

#10


引用 9 楼 king769147 的回复:
Quote: 引用 6 楼 sp1234 的回复:

当你在 == 重载中使用 Equals,你就明白其意思了。假设你的程序根本不会产生死循环,那么你根本无须理会这个编译警告。但是假设有可能产生死循环,那么你可以重新定义你的 Equals 方法,但是其实根本原因还是你在 == 中存在的(可能存在)的死循环。所以这个编译器警告根本是模棱两可的,你用测试就能证实根本不是“需要重载 Equals”的问题,而是编译器建议你要注意别在 == 中写出死循环代码。


知道了...就是防止 递归调用...你说的好多...但是....现在才说到重点哦...

不是防止递归调用……

#11


你根本就没理解我说的什么
这里的关键在于三个判断对象相等性API的行为要一致,和递归什么的根本毫无关系

#1


那个只是警告,没有非要让你重写Equals。既然是只是警告,就不一定靠谱,你不一定要把警告当作问题。

这里就是的。你应该调用 Equals来进行判断 if (p1.Equals(null) || p2.Equals(null)),不应该在 == 重载方法中直接使用 == 运算符。这里的警告只是体现这个建议而已,你不要对编译器警告简单地都当作什么问题对待。

#2


对于编译器警告,不要夸张它。你应该自己测试。

#3


引用 2 楼 sp1234 的回复:
对于编译器警告,不要夸张它。你应该自己测试。


你的意思是,应该这样重写是么?
public static bool operator ==(Person p1, Person p2) {
            //if(p1 == null || p2 == null)
            if(ReferenceEquals(p1, p2))
            throw new NullReferenceException("Person is Null!");
            return (p1.Name == p2.Name && p1.Id == p2.Id);
        }


不是,我没有当它很夸张,只是不理解为什么要重写而已,也就是说,我不知道为什么编译器要提示重写!
sp1234
我查一下资料,说如果 override "==","!=".
编译器会警告,要重写Object 的 Equals() 方法和 HashCode方法.
它解释说: 原因是 Equals 方法应实现与 "=="运算符相同类型的相等逻辑.

这是什么意思哈...帮我解释一下...

#4


不需要重写,只不过你的代码要改为  if (p1.Equals(null) || p2.Equals(null))。

这个编译器警告不靠谱,否则它也不会仅仅是警告了。

因为你在 == 重载中又使用了它自身,所以就算是最傻的程序员如果死记硬背某个条文之后也会说出跟这个编译器警告相同的梦话来。所以说这个警告只不过是另外一个意思,不要死抠它的字眼。

#5


其实我在 #1 楼帮你解释清楚了,你又要求我再次解释一遍。

#6


当你在 == 重载中使用 Equals,你就明白其意思了。假设你的程序根本不会产生死循环,那么你根本无须理会这个编译警告。但是假设有可能产生死循环,那么你可以重新定义你的 Equals 方法,但是其实根本原因还是你在 == 中存在的(可能存在)的死循环。所以这个编译器警告根本是模棱两可的,你用测试就能证实根本不是“需要重载 Equals”的问题,而是编译器建议你要注意别在 == 中写出死循环代码。

#7


==和object.Equals都是用来比较对象的相等性的,它们返回不同的结果会让用户感到困惑。一般来说,它们的行为应该一致[1],所以编译器会提醒你在重载了operator_equality之后要重写object.Equals.
比如你看string,它重载了==运算符,同时就也重写了object.Equals。

除了==和object.Equals,如果你的类型实现了IEquatable<T>,它也应该使用相同的比较逻辑。

如果不这样的话,那就乱套了,三种用于比较类型相等性的API返回三种不同的结果。

至于GetHashCode,由于它是用来在hash表中分组的,所以对于“相同”的对象,GetHashCode应该返回相同的值。你改变了==和object.Equals的行为,那么GetHashCode的行为也应该改变,否则就不能满足这一要求。所以当你你重写了object.Euqals时编译器会提醒你重写GetHashCode。


[1]前面说==和object.Equals应该保持行为一致,有一个例外。
因为C#没有提供判断引用相等的语法,大部分程序员都习惯了使用==而不是Object.ReferenceEquals来判断引用相等,所以C#中的引用类型最好不要重载==,而是要保持它的默认行为。也就是说object.Equals,IEquatable<T>.Equals使用你的自定义比较逻辑,而==保留默认的对象引用比较。

但是反过来,如果你打破这条建议,重载了引用类型的==运算符,那么object.Equals和IEquatable<T>.Equals应该使用和他相同的逻辑。

#8


引用 7 楼 hhddzz 的回复:
==和object.Equals都是用来比较对象的相等性的,它们返回不同的结果会让用户感到困惑。一般来说,它们的行为应该一致[1],所以编译器会提醒你在重载了operator_equality之后要重写object.Equals.
比如你看string,它重载了==运算符,同时就也重写了object.Equals。

除了==和object.Equals,如果你的类型实现了IEquatable<T>,它也应该使用相同的比较逻辑。

如果不这样的话,那就乱套了,三种用于比较类型相等性的API返回三种不同的结果。

至于GetHashCode,由于它是用来在hash表中分组的,所以对于“相同”的对象,GetHashCode应该返回相同的值。你改变了==和object.Equals的行为,那么GetHashCode的行为也应该改变,否则就不能满足这一要求。所以当你你重写了object.Euqals时编译器会提醒你重写GetHashCode。


[1]前面说==和object.Equals应该保持行为一致,有一个例外。
因为C#没有提供判断引用相等的语法,大部分程序员都习惯了使用==而不是Object.ReferenceEquals来判断引用相等,所以C#中的引用类型最好不要重载==,而是要保持它的默认行为。也就是说object.Equals,IEquatable<T>.Equals使用你的自定义比较逻辑,而==保留默认的对象引用比较。

但是反过来,如果你打破这条建议,重载了引用类型的==运算符,那么object.Equals和IEquatable<T>.Equals应该使用和他相同的逻辑。

嗯....你和 6 楼说的都很好了...谢谢...

#9


引用 6 楼 sp1234 的回复:
当你在 == 重载中使用 Equals,你就明白其意思了。假设你的程序根本不会产生死循环,那么你根本无须理会这个编译警告。但是假设有可能产生死循环,那么你可以重新定义你的 Equals 方法,但是其实根本原因还是你在 == 中存在的(可能存在)的死循环。所以这个编译器警告根本是模棱两可的,你用测试就能证实根本不是“需要重载 Equals”的问题,而是编译器建议你要注意别在 == 中写出死循环代码。


知道了...就是防止 递归调用...你说的好多...但是....现在才说到重点哦...

#10


引用 9 楼 king769147 的回复:
Quote: 引用 6 楼 sp1234 的回复:

当你在 == 重载中使用 Equals,你就明白其意思了。假设你的程序根本不会产生死循环,那么你根本无须理会这个编译警告。但是假设有可能产生死循环,那么你可以重新定义你的 Equals 方法,但是其实根本原因还是你在 == 中存在的(可能存在)的死循环。所以这个编译器警告根本是模棱两可的,你用测试就能证实根本不是“需要重载 Equals”的问题,而是编译器建议你要注意别在 == 中写出死循环代码。


知道了...就是防止 递归调用...你说的好多...但是....现在才说到重点哦...

不是防止递归调用……

#11


你根本就没理解我说的什么
这里的关键在于三个判断对象相等性API的行为要一致,和递归什么的根本毫无关系