讀【157個完美化C#的建議】一書的理解筆記 - 011、012
重點:==用於參考比較(記憶體),Equals用於值的比較。Dictionary用HashCode比較
流程說明 |
1. == 與 Equals 差異 |
2. 覆寫Equals 範例 |
3. HashCode是什麼,覆寫範例 |
4. 結論 |
1. == 與 Equals 差異
以下範例可以得知在Object的屬性中,== 一般預設為比較參考位址 ,要確切的比較任何型別的值,保險作法是用Equals
//== 範例- 引用
object A = 1;
object B = 1;
bool result_TestOne = (A == B);//False A 跟 B 是獨立物件,引用不相等----引用
bool result_TestThre = A.Equals(B);// A 與 B 值都是1,所以相等---------值
A = B;
bool result_TestTwo = (A == B);//True A 引用了 B ,所以引用相等 --------引用
bool result_TestFour = A.Equals(B);// A 與 B 值都是1,所以相等 --------值
2. 覆寫Equals 範例
我們建立【個人】的類別,如下:
/// <summary>
/// 個人
/// </summary>
public class Person
{
/// <summary>
/// 身分證
/// </summary>
public string IDCard { get; private set; }
/// <summary>
/// 出生時建立唯一身分證字號
/// </summary>
/// <param name="inputId"></param>
public Person(string inputId)
{
this.IDCard = inputId;
}
/// <summary>
/// Equals 用於比較值 ==用於比較參考(記憶體位址)
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
//比較身分證上的ID
return this.IDCard == (obj as Person).IDCard;
}
}
其中覆寫的Equals 的程式碼如下:
再傳進一個類別時,會強制轉為型別Person(※正確做法請參考 本系列003篇(IS與AS的用法),這邊為了講解省略細節)
並且進行.IDCard的屬性比較。
/// <summary>
/// Equals 用於比較值 ==用於比較參考(記憶體位址)
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
//比較身分證上的ID
return this.IDCard == (obj as Person).IDCard;
}
實際主程式會執行以下(類別直接比較會對記憶體比較,但我們覆寫了Equals):
//== 範例 何時覆寫 Equals
Person person_A = new Person("123");
Person person_B = new Person("123");
//True 因為值相同
bool isEqualAB = person_A.Equals(person_B);
//False 因為記憶體位址不同
bool isMemoryAB = (person_A == person_B);
3. HashCode是什麼,覆寫範例
請參考以下,我們建立一個Dictionary < , >
static Dictionary<Person, PersonDetail> PersonValues = new Dictionary<Person, PersonDetail>();
其中Person 為範例2的宣告,PersonDetail 為個人的文件檔案,如下:
/// <summary>
/// 個人文件資料
/// </summary>
public class PersonDetail
{
public string FileName { get; set; }
}
並且建立以下Function
static void AddPerson()
{
//123這個人
Person person_123 = new Person("123");
//123的個人文件
PersonDetail detail_123 = new PersonDetail() { FileName = "123文件" };
//加入字典中
PersonValues.Add(person_123, detail_123);
//True 表示字典裡有這個人(Person 類別)的資料
bool get = PersonValues.ContainsKey(person_123);
}
在主程式執行以下程式的GetKey會發現 得到False ,但是我們一開始就將值123放入了,為何會有這個結果呢?
原因就是HashCode 不一樣
//========== GetHash Code 到底是什麼 : 每個物件真正的代碼
//靜態建立
AddPerson();
//再次建立123這個人
Person person_123 = new Person("123");
//理論上AddPerson()已經將資料加入靜態函示中所以要從字典找得到
//結果False
bool get = PersonValues.ContainsKey(person_123);
令工程師難以接受,必須有以下覆寫HashCode的做法
我們將Person 類別覆寫GetHashCode,完整Code如下:
/// <summary>
/// 個人
/// </summary>
public class Person
{
/// <summary>
/// 身分證
/// </summary>
public string IDCard { get; private set; }
/// <summary>
/// 出生時建立唯一身分證字號
/// </summary>
/// <param name="inputId"></param>
public Person(string inputId)
{
this.IDCard = inputId;
}
/// <summary>
/// Equals 用於比較值 ==用於比較參考(記憶體位址)
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
//比較身分證上的ID
return this.IDCard == (obj as Person).IDCard;
}
/// <summary>
/// 標準的Equal 必須覆寫Hash Code
/// 沒有HashCode Dictionary 就會找不到對應的值
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
//覆寫IDCard 所以 GetHashCode 也要覆寫 IDCard.GetHashCode()
return this.IDCard.GetHashCode();
}
}
再次執行以下代碼得到 True ,Dictionary 也就可以正常使用了,享受精準又快速的Dictionary的查詢吧~。
//結果True
bool get = PersonValues.ContainsKey(person_123);
4. 結論
比較對象 | 覆寫目的 | |
Equals | 值 | 資料一致性、正確性 |
== | 參考(記憶體) | |
HashCode | Dictionary 唯一Key |
軟體工程中默許 Equals 比較值、==比較記憶體
github連結(Vs2015) : 點我下載