先看如下代碼,猜猜輸出什嗎?
static void Main(string[] args)
{
List<A> list = new List<A>();
list.Add(new A(1, 1));
list.Add(new A(2, 2));
Console.WriteLine(list.Count);
list.Remove(new A(1, 1));
Console.WriteLine(list.Count);
}
其實代碼很簡單,建立一個鏈表,增加2個元素,然後試圖刪除一個元素。在刪除前後列印出鏈表中元素的個數.
然而結果卻是:不一定!
問題的關鍵就是
list.Remove(new A(1, 1));
看似這句的企圖是,刪除第一個元素,那麼它能達到這個目的嗎?這要看看,list.Remove(T)的實現原理了,首先要檢查鏈表中是否有這個元素,然後才能刪除。那麼,怎麼確定是否有這個元素呢?看MSDN的描述:
此方法使用用於 T(列表中的值的類型)的預設相等比較子 EqualityComparer(T).Default 來確定相等性。
那麼我們看看這個EqualityComparer(T).Default 是什嗎?跟蹤MSDN如下:
返回一個預設的相等比較子,用於比較此泛型參數指定的類型。
Default 屬性檢查類型 T 是否實現此 System.IEquatable(T) 泛型介面,如果實現,該屬性將返回一個使用該實現的 EqualityComparer(T)。否則,它返回一個使用 T 提供的 Object.Equals 和 Object.GetHashCode 的重寫的 EqualityComparer(T)。
判斷相等並不簡單,如果什麼都不做的話,預設會使用Object.Equals()函數判斷相等。而我們知道Object.Equals()其實實現的是同一性比較,而不是相等性比較。所以只有兩個引用變數指向同一個對象,才判定為相等。然而上述代碼中,顯然new A(1,1)返回的引用變數不可能指向鏈表中引用變數指向的對象,所以輸出結果會是:
2
2
然而,如果我們的A,覆蓋了Object的Equals函數,例如:
class A
{
public int x;
public int y;
public A(int _x, int _y)
{
x = _x;
y = _y;
}
public static bool operator == (A a1, A a2)
{
Console.WriteLine("operator ==");
return (a1 == a2);
}
public static bool operator != (A a1, A a2)
{
return (a1 != a2);
}
public override bool Equals(object obj)
{
Console.WriteLine("override Equals");
if (this.x == ((A)obj).x && this.y == ((A)obj).y)
return true;
else
return false;
}
}
那麼輸出結果將會是
2
1
所以,list.Remove(T)完全依賴於T的實現。上述代碼也證明了,List.Remove(T)不會使用T的==操作符而是使用T的Equals()方法判斷相等性。