理解CSharp 語言中相等Equality 和唯一 Identity

c#有一個“Equals”方法,可以用來比較兩個對象。我將試著用例子來解釋等式和同一性的概念。

  1. namespace TestEqualityDemo
  2. {
  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  7. TestEquality test1 = new TestEquality();
  8. test1.FirstName = "Tom";
  9. test1.FirstName = "Cruise";
  10. TestEquality test2 = new TestEquality();
  11. test2.FirstName = "Tom";
  12. test2.FirstName = "Cruise";
  13. TestEquality test3 = test2;
  14. bool areEqual = test1.Equals(test2);
  15. System.Console.WriteLine("Are test1 and test2 are Equal:" + areEqual);
  16. areEqual = test2.Equals(test3);
  17. System.Console.WriteLine("Are test2 and test3 are Equal:" + areEqual);
  18. System.Console.ReadLine();
  19. }
  20. }
  21. class TestEquality
  22. {
  23. public string FirstName { get; set; }
  24. public string LastName { get; set; }
  25. }
  26. }

輸出:

  1. Are test1 and test2 Equal:False
  2. Are test2 and test3 Equal:True

在上面的例子中

即使test1和test2包含FirstName和LastName的值相同,“Equals”方法也返回False。這是因為Equals方法的默認實現不檢查是否相等;它檢查Identity(對象引用地址)。這意味著test1和test2必須引用完全相同的對象,然後只有它返回True,否則,它將返回False。

當test2和test3引用相同的對象時,它返回True。根據上面的程序,我們可以得出等號的默認實現,這意味著只有當兩個變量指向同一個對象時,它才會返回True。

然後,出現瞭如何在c#中檢查等式的問題,答案是覆蓋Equals方法的默認實現。

這裡是Equals方法的默認實現。

  1. public virtual bool Equals(Object obj)
  2. {
  3. //If both the references points to the same object then only return true
  4. if (this == obj)
  5. {
  6. return
    true;
  7. }
  8. return false;
  9. }

必須考慮以下幾點來覆蓋Equals方法,

如果obj參數為空,則返回false

如果this和obj引用相同的對象,則返回True。這可以在與許多字段進行比較時提高性能

如果this和obj指的是不同的類型,則返回False,因為沒有必要比較不同類型的對象;例如,如果我們比較一個字符串對象和DateTime對象,因為它們在任何情況下都不相等。

將該對象的每個字段與obj對象的相應字段進行比較。

最後,調用基類=方法。

以上是重寫Equals方法進行比較的最佳步驟。

讓我們為TestEquality類實現Equals方法:

  1. namespace TestEqualityDemo
  2. {
  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  7. TestEquality test1 = new TestEquality();
  8. test1.FirstName = "Tom";
  9. test1.FirstName = "Cruise";
  10. TestEquality test2 = new TestEquality();
  11. test2.FirstName = "Tom";
  12. test2.FirstName = "Cruise";
  13. TestEquality test4 = new TestEquality();
  14. test2.FirstName = "Will";
  15. test2.FirstName = "Smith";
  16. TestEquality test3 = test2;
  17. bool areEqual = test1.Equals(test2);
  18. System.Console.WriteLine("Are test1 and test2 Equal:" + areEqual);
  19. areEqual = test2.Equals(test3);
  20. System.Console.WriteLine("Are test2 and test3 Equal:" + areEqual);
  21. System.Console.ReadLine();
  22. areEqual = test2.Equals(test4);
  23. System.Console.WriteLine("Are test2 and test4 Equal:" + areEqual);
  24. System.Console.ReadLine();
  25. }
  26. }
  27. class TestEquality
  28. {
  29. public string FirstName { get; set; }
  30. public string LastName { get; set; }
  31. public override
    bool Equals(System.Object obj)
  32. {
  33. //If the obj argument is null return false
  34. if (obj == null)
  35. return false;
  36. //If both the references points to the same object then only return true
  37. if (this == obj)
  38. return true;
  39. //If this and obj are referring to different type return false
  40. if (this.GetType() != obj.GetType())
  41. return false;
  42. //Compare each field of this object with respective field of obj object
  43. TestEquality test = (TestEquality)obj;
  44. if (this.FirstName == test.FirstName &&
  45. this.LastName == test.LastName)
  46. {
  47. return true;
  48. }
  49. return false;
  50. }
  51. }
  52. }

輸出:

  1. Are test1 and test2 Equal:True
  2. Are test2 and test3 Equal:True
  3. Are test2 and test4 Equal:False

在上面的程序中,

test1和test2的所有字段都相等,所以Equals方法返回true。

test2和test3引用同一個對象,因此它也會返回false。

在test2和test4中,如果FirstName和LastName的值不同,則返回false

重寫的Equals方法必須遵循下面的規則:

=必須是自反的,也就是x.Equals(x)必須返回true。

=必須是對稱的,即 x.Equals(y)必須返回與 y.Equals(x)相同的值

等號必須是傳遞i。如果x.Equals(y) 返回true, y.Equals(z) 返回true,那麼x.Equals(z)必須返回true。

如果重寫的Equals方法不遵循上述規則,那麼您的應用程序可能會中斷或產生意外結果。

另外,在C#裡為什麼重載了Equals()就要重載GetHashCode()?發現如果只重載Equals的話,編譯器會給一個警告:

warning CS0659: 'UseCom.Program.b' overrides Object.Equals(object o) but does not override Object.GetHashCode()

在解釋這個問題之前需要先把Equals()和GetHashCode()方法進行深入瞭解。

首先先談一下Equals()這個方法:

Equals()方法,來自於Object,是我們經常需要重寫的方法。此方法的默認實現大概是這樣的:

public virtual bool Equals(object obj){  if(obj==null) return false;  if(GetType() != obj.GetType()) return false;  return true;}

在很多地方都是特殊處理的,此處就不做深究了。

Ps:按Jeffrey Richter的說法,在值類型使用Equals()時,因為Equals()使用了反射,在比較時會影響效率。

說完Equals()後再來聊一聊GetHashCode()。

其實GetHashCode()在操作值類型的時候也是被System.ValueType()重寫的。經過樓主測試的幾個常用值類型來看,值類型的GetHashCode()基本都是原值輸出(特指整數,Int32除外),真實性有待驗證。

結果如下:

理解CSharp 語言中相等Equality 和唯一 Identity

說完值類型,說一下引用類型,先看下面這張運行結果:

理解CSharp 語言中相等Equality 和唯一 Identity

從上圖的結果可以看出,雖然string是引用類型,但是隻要值一樣,返回的HashCode也是一樣的,這取決於它的特殊性。而我們自己寫的類型Coordinates同樣的值但返回的HashCode卻不一樣,我們可以簡單的理解為是coor1與coor2的內存地址不同,所以CLR認為它們是不一樣的。

Ps:在程序的生命週期中,相同的對象、變量返回的HashCode是相同的,並且是唯一的。但是絕對不允許做持久性存儲,程序一旦結束並重新啟動後,同樣的對象無法獲得上次程序運行時的HashCode。

瞭解了兩個方法後,開始今天的重點話題。

其實在上面的兩個對象中(coor1、coor2),coor1.Equals(coor2)的返回結果為false(因為內存地址不同),如果我們想讓它們的返回結果為true的話,只能重寫Equals方法(如下圖)。

理解CSharp 語言中相等Equality 和唯一 Identity

重點來了,重寫完Equals以後,vs發出了警告,雖然程序猿從來都是無視警告的,但這個警告確實有必要了解一下,先來看下面這三段代碼。

代碼段一、二:

理解CSharp 語言中相等Equality 和唯一 Identity

代碼段三:

理解CSharp 語言中相等Equality 和唯一 Identity

看完這三段代碼,應該就理解為什麼要重寫Equal時有必要重寫GetHashCode了。

當然,如果你沒打算在代碼中使用Dictionary或HashTable就無所謂寫不寫了,換句話說,如果要把引用類型做為Dictionary或HashTable的key使用時,必須重寫這兩個方法。

。其結果就是,存儲的時候你可能任性的存,在取值的時候就是你哭著找不著娘了。

好了,說了這麼多你應該對這兩個方法有了重新的認識了吧。如果還是不明白的話,用代碼實現一下,保準明白。

http://www.cnblogs.com/xiaochen-vip8/articles/5506478.html


分享到:


相關文章: