今天在写代码的时候遇到这样一个问题:自定义了一个类,为了在对两个实例比较时方便,所以重载了==运算符,当然也同样重载了!=运算符。在测试的时候却出现了stack overflow的问题。
代码如下(当然不是真正的代码,但是能说明问题):
public class Dog { private string name; public Dog(string dogName) { this.name = dogName; } // 以狗的名字作为标识,假定两只狗叫同一个名字就是同一只狗 public static bool operator == (Dog a, Dog b) { if (a == null && b == null) return true; // 用于判断 if (dog == null)这种情况 if (a == null || b == null) return false; return a.name == b.name; } public static bool operator != (Dog a, Dog b) { return !(a == b); } public override bool Equals(object obj) { Dog dog = obj as Dog; if (dog == null) return false; return this.name == dog.name; } public override int GetHashCode() { return base.GetHashCode(); } }
Main方法如下:
public static void Main() { Dog wangcaiA = new Dog("旺财"); Dog wangcaiB = new Dog("旺财"); Dog laifu = new Dog("来福"); Dog nullDog = null; Console.WriteLine("wangcaiA == wangcaiB : " + (wangcaiA == wangcaiB)); Console.WriteLine("wangcaiA == laifu : " + (wangcaiA == laifu)); Console.WriteLine("wangcaiA == nullDog : " + (wangcaiA == nullDog)); Console.WriteLine("nullDog == nullDog : " + (nullDog == nullDog)); }
运行代码会发现直接死掉,debug会发现是stack overflow。仔细看看代码,死的有理。。。
因为重载了==运算符,所以在判断if (a == null && b == null) 时,会使用重载后运算符的逻辑,然后再去判断a == null然后再走,然后。。。循环到死。
没办法,把书打开看看,发现书里倒是有重载==的例子,但是没有判断是否为null。接着去Google,我估计应该有相关的文章,但是可能关键字搜的不对,总是找不到想要的东西。然后退而求其次,找找有没有不需要通过if (a == null)这种方式来判断一个对象是否为空的方法,可惜还是没有找到,不过在一个国外的论坛上看到一个老外回答别人的问题时留下了这样一行代码: if (this.Equals(null)) {…}。
仔细想想,貌似String类是实现了==运算符的重载的,于是打开reflector看了下源码,代码如下:
public static bool operator ==(string a, string b) { return Equals(a, b); } public static bool operator !=(string a, string b) { return !Equals(a, b); }
Equals方法的代码如下:
public static bool Equals(string a, string b) { return ((a == b) || (((a != null) && (b != null)) && EqualsHelper(a, b))); }
看起来似乎也不对啊,也进行null判断了啊。不管,照着搬过来再说,一运行,果然——还是不行。。。不知道是我代码没看明白还是.net对String做了特殊处理,反正是没起作用。
最后找了个同事帮忙,倒是没有想到什么办法,不过聊到前两天写的一段代码的时候到给了我一点思路,于是换了一种方式来判断一个对象是否为null 。
public static bool operator == (Dog a, Dog b) { bool aIsNull = !(a is object); bool bIsNull = !(b is object); if (aIsNull && bIsNull) return true; if (aIsNull || bIsNull) return false; return a.name == b.name; }
通过 a is object来判断一个对象是否为空。因为所有的对象都直接或间接地继承自object类,而null自然不在此列,所以如果a是null的话,那么a is object自然会返回false,其它的情况都会返回true。
再次运行Main方法,嗯,终于得到想要的结果了。