Identity - 兩個變數是否參考到同一個物件
Equality - 兩個變數參考到的物件的值是不是相等
System.Object.Equals() 基本上實做的是 Identity,他的 code 大概長這樣:
public class Object {
public virtual Boolean Equals(Object rhs) {
if(this == rhs) return true;
return false;
}
}
由於子代有可能 override System.Object.Equals() 以及各種比較算符 (== 或 !=),因此若要判斷兩個變數是否參考到同一個物件 (Identity),請不要使用 Equals() 或 ==,請使用 Object.ReferenceEquals(),他的 code 大概長這樣:
public class Object {
public static Boolean ReferenceEquals(Object lhs, Object, rhs) {
return (lhs == rhs);
}
}
System.ValueType 是 System.Object 的子代,它 override 了 Equals() 這個 method,讓它變成實作 Equality。但由於 System.ValueType.Equals() 是靠 reflection 來實作 Equals(),因此它的效率很差。因此當程式設計師設計了自己的 Value Type,而且該 ValueType 有比較的需求時,最好提供一個 override 的 Equals() method。
大部份的 class 都沒有比較 Equality 的需求,因此不太需要 override Equals()。但 Value Type 通常會有 override Equals() 的需求。當您要 override Equals() 時,請注意下列事項:
* Reflexive -> x.Equals(x) 必須是 true
* Symmetric -> 若 x.Equals(y), 則 y.Equals(x) 必須為 true
* Transitive -> 若 x.Equals(y) 且 y.Equals(z),則 x.Equals(z) 必須為 true
* Consistent -> 只要物件沒被改變,兩次 Equals() 的結果應該要一樣。
以下是通常要做的額外項目:
* 您應該順便繼承及實作 System.IEquatable<T>
* 您應該要 override GetHashCode(),並且保證:
# 若兩個物件 Equals(),他們的 GetHashCode() 的回傳值必須相同。
# 只要物件未被更改,兩次 GetHashCode() 的回傳值必須相同。
* 您應該順便實作 == 算符與 != 算符
* 如果您的 type 可以被排序:
# 您應該繼承及實做 System.IComparable
# 您應該繼承及實做 System.IComparable<T>
# 您應該實做所有的比大小算符,包含 <, <=, >, >=
以下是一個 override Equals 的範例. 為求簡單, 我只專注在 Equals 以及 GetHashCode 這兩個 method 上, 請務必仔細研讀!!!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1 {
class Program {
class Base {
private Int32 val;
public Base(Int32 val) { this.val = val; }
// 因為父類別是 Object, 他的 Equals 其實是檢查 identity
// 所以在這裡不應該呼叫
public override bool Equals(object obj) {
if(obj == null) return false;
if(Object.ReferenceEquals(this, obj)) return true;
if(this.GetType() != obj.GetType()) return false;
if(this.val != ((Base)obj).val) return false;
return true;
}
// 因為父類別是 Object,
// 他的 GetHashCode 其實是透過 this 指標的位址求得,
// 故在這裡不應該呼叫
public override int GetHashCode() {
return this.val.GetHashCode();
}
};
class Drived : Base {
private Int32 val;
public Drived(Int32 valOfDrived, Int32 valOfBase) : base(valOfBase) {
this.val = valOfDrived;
}
// 請注意!!! 父類別是否是 System.Object 會影響到 Equals 的實作方式
public override bool Equals(object obj) {
if(!base.Equals(obj)) return false;
if(this.val != ((Drived)obj).val) return false;
return true;
}
// 請注意!!! 父類別是否是 System.Object 會影響到 GetHashCode 的實作方式
public override int GetHashCode() {
return base.GetHashCode() ^ this.val.GetHashCode();
}
}
static void Main(string[] args) {
Base b = new Base(1);
Console.WriteLine(b.Equals(new Base(1)));
Console.WriteLine(b.Equals(new Base(2)));
Console.WriteLine(b.Equals(new Drived(2, 1)));
Console.WriteLine(b.Equals(b));
Console.WriteLine(b.Equals(null));
Console.WriteLine("------------------------------");
Drived d = new Drived(2, 1);
Console.WriteLine(d.Equals(new Base(1)));
Console.WriteLine(d.Equals(new Drived(1, 1)));
Console.WriteLine(d.Equals(new Drived(2, 1)));
Console.WriteLine(d.Equals(d));
Console.WriteLine(d.Equals(null));
Console.ReadKey();
}
}
}
沒有留言:
張貼留言