跟同事討論到自訂類別物件的比對問題,原則上Reference Type類別的物件,除非兩個變數指向同一個Instance,使用==或Equals(...)測試都會得到false,就算是兩個Instance的內容分毫不差也是枉然。當物件被當成WCF/Web Service參數來回傳送,背地裡會被序列化再還原,便會變成內容相同的另一個Instance(其實只要在不同Process間傳遞,因無法共享記憶體,就一定會產生這種結果),此時若直接使用==或Equals比對,得到的結果永遠為不相同。

我們可以透過覆寫Equals及==、!=運算子的方式,將"兩個物件是否相同"的定義調整為自訂比對條件。過去只知其然,今天就順道實作一個測試來驗證。

在以下程式中,我宣告了兩個類別: 一般寫法的MyClass及精心特調的HiClass(多寫了好多Code,所以變得比較高級 XD),HiClass實作了Equals、==及!=,透過比對Id屬性來決定物件是否相等。

using System;
public class CSharpLab
{
    class MyClass 
    { 
        public string Id;
        public MyClass(string id) 
        {
            Id = id;
        }
    }
    class HiClass
    { 
        public string Id;
        public HiClass(string id) 
        {
            Id = id;
        }
//REF:http://msdn.microsoft.com/en-us/library/ms173147%28VS.80%29.aspx
        public override bool Equals(object obj) {
            if (obj == null) return false;
            HiClass t = obj as HiClass;
            if ((object)t == null) return false;
            return t.Id == this.Id;
        }
        public bool Equals(HiClass c) {
            if ((object)c == null) return false;
            return this.Id == c.Id;
        }
        public override int GetHashCode() 
        {
            return this.Id.GetHashCode();
        }
        public static bool operator ==(HiClass a, HiClass b)
        {
            if (object.ReferenceEquals(a, b)) return true; 
            if ((object)a == null || (object)b == null) 
                return false;
            return a.Equals(b);
        }
        public static bool operator !=(HiClass a, HiClass b) 
        {
            return !(a==b);
        }
    }
    public static void Test()
    {
        MyClass a = new MyClass("A");
        MyClass b = new MyClass("A");
        MyClass c = a;
        Console.WriteLine("a == b -> {0}", a == b);
        Console.WriteLine("a.Equals(b) -> {0}", a.Equals(b));
        Console.WriteLine("a == c -> {0}", a == c);
        Console.WriteLine("a.Equals(c) -> {0}", a.Equals(c));
        
        HiClass d = new HiClass("A");
        HiClass e = new HiClass("A");
        Console.WriteLine("d == e -> {0}", d == e);
        Console.WriteLine("d.Equals(e) -> {0}", d.Equals(e));
        Console.WriteLine("object.ReferenceEquals(d, e) -> {0}", 
            object.ReferenceEquals(d, e));
    }
}

測試結果如下,大家看看是否符合自己的預期,若一切都在你掌握中,就算差不多已搞懂Reference Type的比對原則了。
a == b -> False
a.Equals(b) -> False
a == c -> True
a.Equals(c) -> True
d == e -> True
d.Equals(e) -> True
object.ReferenceEquals(d, e) -> False


Comments

# by hunterpo

黑大您好, 觀察您的 HiClass 複寫 operator == 那一段: "...|| (object)a == null || (object)b == null)" 感覺較為不合理,再比對 您提供的 msdn 文件內容,它也是檢查 a 或 b 只有一為 null 即回傳 false。 雖說不影響最後測試結果啦...

# by Jeffrey

to hunterpo, 抱歉,程式寫到昏頭了,發現原來的寫法確實有問題,已經修改並感謝您的指正!!

# by Ark

感覺這一課和struct 放一起會更有fu 改成 struct struct mySt { public string Id; public mySt(string id) { Id = id; } } mySt x = new mySt("A"); mySt y = new mySt("A"); x.Equals(y) -> True

Post a comment