2017年9月11日 星期一

C# : 物件相等 Object Equal 的測試


了解更多關於 [Object.Equals Method
了解更多關於 [C# 程式設計手冊 

在 C# 中,我們知道由類別實例化所生成的執行個體(Instance)或稱物件(Object),在進行物件相等比較的時候,使用的參考位址比較,也就是說,若兩個物件變數由類別生成的物件若說他們相等,則表示他們兩個物件變數指向同一個參考物件。可是,由於結構是屬於實值型別(Value Type),所以,當要進行兩個由結構所生成的物件的時候,預設的相等比較是會進行該結構成員內的逐一進行相等比對,在這份筆記中,我們來實際檢測這樣的特色。
在這裡,我們定義兩個結構
  • MyStruct
    這個結構內,宣告了兩個屬性,並且透過了建構函式進行這兩個屬性的內容初始化。
  • MyStructWithEqualOperator
    這個結構內,同樣的,宣告了兩個屬性,並且透過了建構函式進行這兩個屬性的內容初始化。不過,這裡使用了多載運算子 public static bool operator == 與 public static bool operator != 的宣告,進行宣告了這個結構可以使用的新的 == / != 運算子的處理邏輯。在 == 運算子中,我們使用了結構物件(這裡使用的是 MyStructWithEqualOperator 型別的物件,而不是 object) 的 Equals 方法來進行兩個物件的比較。
public struct MyStruct
{
    public int X { get; set; }
    public int Y { get; set; }
    public MyStruct(int x, int y)
    {
        X = x;
        Y = y;
    }
}
    public struct MyStructWithEqualOperator
    {
        public int X { get; set; }
        public int Y { get; set; }
        public MyStructWithEqualOperator(int x, int y)
        {
            X = x;
            Y = y;
        }

        public static bool operator ==(MyStructWithEqualOperator c1, MyStructWithEqualOperator c2)
        {
            return c1.Equals(c2);
        }

        public static bool operator !=(MyStructWithEqualOperator c1, MyStructWithEqualOperator c2)
        {
            return !c1.Equals(c2);
        }
    }
另外,我們宣告了類別 MyClass,這個類別如同上述的結構一樣,有兩個屬性,可以透過建構式來進行初始化。
public class MyClass
{
    public int X { get; set; }
    public int Y { get; set; }
    public MyClass(int x, int y)
    {
        X = x;
        Y = y;
    }
}

進行測試 類別物件比較

首先我們要進行的測試為類別的物件比較,在這裡,我們產生兩個類別物件,這兩個物件擁有同樣的屬性值(這兩個屬性屬於 int 的型別),不過,這兩個物件在堆積(Heap)是分別佔據不同的記憶體空間,也可以說,這兩個物件變數分別指向了兩個不同實體。相關測試的程式碼如下所示。
Console.WriteLine("參考型別的物件比較");
MyClass classObject1 = new MyClass(10, 20);
MyClass classObject2 = new MyClass(10, 20);
MyClass classObject3;
classObject3 = classObject2;

Console.WriteLine($"classObject1==classObject2 is {classObject1 == classObject2}");
Console.WriteLine($"classObject1.Equals(classObject2) is {classObject1.Equals(classObject2)}");
Console.WriteLine($"object.Equals(classObject1, classObject2) is {object.Equals(classObject1, classObject2)}");
Console.WriteLine($"object.ReferenceEquals(classObject1, classObject2) is {object.ReferenceEquals(classObject1, classObject2)}");

Console.WriteLine("比較參考到同一個實體物件");
Console.WriteLine($"classObject3==structObject2 is {classObject3 == classObject2}");
Console.WriteLine($"classObject3.Equals(classObject2) is {classObject3.Equals(classObject2)}");
Console.WriteLine($"object.Equals(classObject3, classObject2) is {object.Equals(classObject3, classObject2)}");
Console.WriteLine($"object.ReferenceEquals(classObject3, classObject2) is {object.ReferenceEquals(classObject3, classObject2)}");
Console.WriteLine("Press any key for continuing...");
Console.ReadKey();
接著,我們進行了底下的測試,這個是關於兩個不同參考物件(但是成員屬性內容相同)的比較
  • classObject1==classObject2
    這裡使用了靜態運算子方法來進行這兩個不同實體比較,因為預設為參考比較,所以得到的結果為 False。
  • classObject1.Equals(classObject2)
    這裡使用物件本身的 Equals 方法來進行不同實體物件比較,因為預設為參考比較,所以得到的結果為 False。
  • object.Equals(classObject1, classObject2)
    這裡使用最上層類別,object 的 Equals 靜態方法來進行不同實體物件比較,因為預設為參考比較,所以得到的結果為 False。
  • object.ReferenceEquals(classObject1, classObject2)
    這裡使用最上層類別,object 的 ReferenceEquals 靜態方法來進行不同實體物件比較,所以得到的結果為 False。
這裡是這個測試的執行結果
參考型別的物件比較
structObject1==structObject2 is False
classObject1.Equals(classObject2) is False
object.Equals(classObject1, classObject2) is False
object.ReferenceEquals(classObject1, classObject2) is False
然後,我們進行了底下的測試,這個是關於兩個相同參考物件的比較,我們使用敘述 classObject3 = classObject2; 將 classObject3 與 classObject2 兩個物件變數設定成為參考到同一個物件實體。
  • classObject3==structObject2
    這裡使用了靜態運算子方法來進行這兩個相同實體比較,因為預設為參考比較,所以得到的結果為 True。
  • classObject3.Equals(classObject2)
    這裡使用物件本身的 Equals 方法來進行相同實體物件比較,因為預設為參考比較,所以得到的結果為 True。
  • object.Equals(classObject3, classObject2)
    這裡使用最上層類別,object 的 Equals 靜態方法來進行相同實體物件比較,因為預設為參考比較,所以得到的結果為 True。
  • object.ReferenceEquals(classObject3, classObject2)
    這裡使用最上層類別,object 的 ReferenceEquals 靜態方法來進行相同實體物件比較,所以得到的結果為 True。
這裡是這個測試的執行結果
比較參考到同一個實體物件
classObject3==structObject2 is True
classObject3.Equals(classObject2) is True
object.Equals(classObject3, classObject2) is True
object.ReferenceEquals(classObject3, classObject2) is True

進行測試 結構物件比較

現在,我們要來測試結構的物件比較,在這裡,我們使用建構式,建立了兩個結構物件。
Console.WriteLine("結構型別的物件比較");
MyStruct structObject1 = new MyStruct(10, 20);
MyStruct structObject2 = new MyStruct(10, 20);
MyStruct structObject3;
structObject3 = structObject2;

//Console.WriteLine($"structObject1==structObject2 is {structObject1 == structObject2}");
Console.WriteLine($"structObject1.Equals(structObject2) is {structObject1.Equals(structObject2)}");
Console.WriteLine($"object.Equals(structObject1, structObject2) is {object.Equals(structObject1, structObject2)}");
Console.WriteLine($"object.ReferenceEquals(structObject1, structObject2) is {object.ReferenceEquals(structObject1, structObject2)}");
Console.WriteLine("Press any key for continuing...");
Console.ReadKey();
接著,我們進行了底下的測試,這個是關於兩個不同參考物件(但是成員屬性內容相同)的比較
  • structObject1==structObject2
    當您使用這個敘述的時候,Visual Studio 會回報您有錯誤產生。
    錯誤 CS0019 運算子 '==' 不可套用至類型為 'MyStruct' 和 'MyStruct' 的運算元 ObjectEqual
    所以,若您沒有做任何多載 == 運算子的程式碼,您是無法在客製化結構中,使用這個運算子。
  • structObject1.Equals(structObject2)
    這裡使用物件本身的 Equals 方法來進行不同實體物件比較,因為預設為實值比較,所以會針對該結構內的成員進行實際擁有值的比較,所以得到的結果為 True
  • object.Equals(structObject1, structObject2)
    這裡使用最上層類別,object 的 Equals 靜態方法來進行不同實體物件比較,因為預設為實值比較,所以會針對該結構內的成員進行實際擁有值的比較,所以得到的結果為 True
  • object.ReferenceEquals(structObject1, structObject2)
    這裡使用最上層類別,object 的 ReferenceEquals 靜態方法來進行不同實體物件比較,所以得到的結果為 False。
這裡是這個測試的執行結果
結構型別的物件比較
structObject1.Equals(structObject2) is True
object.Equals(structObject1, structObject2) is True
object.ReferenceEquals(structObject1, structObject2) is False

進行測試 結構物件比較(有多載 == != 運算子)

在上面的測試中,我們看到了,結構預設無法使用 == 或者 != 運算子來進行物件內容比較,所以,在這個測試中,我們使用了客製化結構 MyStructWithEqualOperator,在這個結構內,有多載 == != 運算子的宣告,讓我們可以在結構的 == 與 != 運算子,來進行物件的比較。
Console.WriteLine("結構型別的物件比較(有多載等於與不等於的運算子)");
MyStructWithEqualOperator structWithEqualOperatorObject1 = new MyStructWithEqualOperator(10, 20);
MyStructWithEqualOperator structWithEqualOperatorObject2 = new MyStructWithEqualOperator(10, 20);
MyStructWithEqualOperator structWithEqualOperatorObject3;
structWithEqualOperatorObject3 = structWithEqualOperatorObject2;

Console.WriteLine($"structWithEqualOperatorObject1==structWithEqualOperatorObject2 is {structWithEqualOperatorObject1 == structWithEqualOperatorObject2}");
Console.WriteLine($"structWithEqualOperatorObject1.Equals(structWithEqualOperatorObject2) is {structWithEqualOperatorObject1.Equals(structWithEqualOperatorObject2)}");
Console.WriteLine($"object.Equals(structWithEqualOperatorObject1, structWithEqualOperatorObject2) is {object.Equals(structWithEqualOperatorObject1, structWithEqualOperatorObject2)}");
Console.WriteLine($"object.ReferenceEquals(structWithEqualOperatorObject1, structWithEqualOperatorObject2) is {object.ReferenceEquals(structWithEqualOperatorObject1, structWithEqualOperatorObject2)}");
Console.WriteLine("Press any key for continuing...");
Console.ReadKey();

經過多載 == != 運算子之後,結構所產生的執行個體,就可以進行使用這兩個運算子了
結構型別的物件比較(有多載等於與不等於的運算子)
structWithEqualOperatorObject1==structWithEqualOperatorObject2 is True
structWithEqualOperatorObject1.Equals(structWithEqualOperatorObject2) is True
object.Equals(structWithEqualOperatorObject1, structWithEqualOperatorObject2) is True
object.ReferenceEquals(structWithEqualOperatorObject1, structWithEqualOperatorObject2) is False

了解更多關於 [Object.Equals Method
了解更多關於 [C# 程式設計手冊 











沒有留言:

張貼留言