2008-09-23 73 views
3

目前我有這個(閱讀意見後編輯):如何在不可變的泛型Pair結構上實現IEqualityComparer?

struct Pair<T, K> : IEqualityComparer<Pair<T, K>> 
{ 
    readonly private T _first; 
    readonly private K _second; 

    public Pair(T first, K second) 
    { 
     _first = first; 
     _second = second; 

    } 

    public T First { get { return _first; } } 
    public K Second { get { return _second; } } 

    #region IEqualityComparer<Pair<T,K>> Members 

    public bool Equals(Pair<T, K> x, Pair<T, K> y) 
    { 
     return x.GetHashCode(x) == y.GetHashCode(y); 
    } 

    public int GetHashCode(Pair<T, K> obj) 
    { 
     int hashCode = obj.First == null ? 0 : obj._first.GetHashCode(); 

     hashCode ^= obj.Second == null ? 0 : obj._second.GetHashCode(); 

     return hashCode; 
    } 

    #endregion 

    public override int GetHashCode() 
    { 
     return this.GetHashCode(this); 
    } 

    public override bool Equals(object obj) 
    { 
     return (obj != null) && 
    (obj is Pair<T, K>) && 
    this.Equals(this, (Pair<T, K>) obj); 
    } 
} 

的問題是,第一,第二可能不是引用類型(VS實際上提醒我這件事),但代碼仍然編譯。在比較它們之前,我是否應該將它們(第一和第二)投射到物體上,還是有更好的方法來做到這一點?

編輯: 請注意,我想結構,支持值類型和引用類型(換句話說,按類限制是不是一個有效的解決方案)

編輯2: 至於什麼我想要實現,我希望這個工作在一個詞典。其次,SRP對我來說現在並不重要,因爲這不是這個問題的實質 - 它總是可以在以後重構。第三,與默認值(T)相比,不會比較null值 - 嘗試它。

回答

2

它看起來像你需要IEquatable代替:

internal struct Pair<T, K> : IEquatable<Pair<T, K>> 
{ 
    private readonly T _first; 
    private readonly K _second; 

    public Pair(T first, K second) 
    { 
    _first = first; 
    _second = second; 
    } 

    public T First 
    { 
    get { return _first; } 
    } 

    public K Second 
    { 
    get { return _second; } 
    } 

    public bool Equals(Pair<T, K> obj) 
    { 
    return Equals(obj._first, _first) && Equals(obj._second, _second); 
    } 

    public override bool Equals(object obj) 
    { 
    return obj is Pair<T, K> && Equals((Pair<T, K>) obj); 
    } 

    public override int GetHashCode() 
    { 
    unchecked 
    { 
     return (_first != null ? _first.GetHashCode() * 397 : 0)^(_second != null ? _second.GetHashCode() : 0); 
    } 
    } 
} 
2

您的IEqualityComparer實現應該是一個不同的類(並且肯定不是一個結構,因爲您要重用該引用)。

此外,您的散列碼不應該被緩存,因爲結構的默認GetHashcode實現(您不會覆蓋)會將該成員考慮在內。

0

關於警告,您可以使用默認(T)和默認(K)而不是null。

我看不到你要達到的目標,但是你不應該使用哈希碼來比較是否相等 - 不能保證兩個不同的目標不會有相同的哈希碼。即使你的結構是不可變的,成員_first和_second也不是。

+0

是否所有內置值類型已經0的哈希碼默認值嗎? – ilitirit 2008-09-23 13:13:22

+0

如果不是這種情況,我會很驚訝,但不想保證。 – Joe 2008-09-23 13:31:49

0

首先,這段代碼違反了SRP原則。 Pair類用於保存對,如果項目,對不對?將相等功能委託給它是不正確的。

接下來,讓我們看看你的代碼:

的Equals如果其中一個參數爲null方法將失敗 - 沒有好。 Equals使用Pair類的哈希碼,但看看GetHashCode的定義,它只是對成員哈希碼的組合 - 它與項目的等式無關。我期望Equals方法將比較實際數據。不幸的是,我現在太忙,無法提供正確的實施。但從第一眼看,你的代碼似乎是錯誤的。如果您向我們提供您想要實現的內容的描述會更好。我相信SO會員將能夠給你一些建議。

0

可能我建議使用Lambda表達式作爲參數嗎? 這將允許您指定如何比較內部通用類型。

0

編譯有關這個時,我沒有收到任何警告,但我認爲你是在談論==空比較?演員似乎會讓這一切變得更清潔,是的。

PS。你真的應該爲比較器使用一個單獨的類。這個填充兩個角色(成對和比較對象)的類很醜陋。

1

如果在比較方法時使用了hashcode,如果哈希碼相同,則應該檢查「realy value」。

bool result = (x._hashCode == y._hashCode); 
if (result) { result = (x._first == y._first && x._second == y._second); } 
// OR?: if (result) { result = object.Equals(x._first, y._first) && object.Equals(x._second, y._second); } 
// OR?: if (result) { result = object.ReferenceEquals(x._first, y._first) && object.Equals(x._second, y._second); } 
return result; 

但是,比較「_first」和「_second」字段有一點點問題。 默認引用類型使用前等同比較「object.ReferenceEquals」方法,他們可以重寫它們。所以正確的解決方案取決於「究竟應該做什麼」你的比較方法。應該使用「_first」&「_second」字段或object.ReferenceEquals的「Equals」方法嗎?還是更復雜的東西?