2008-09-23 4 views
3

현재 나는이 (조언을 읽은 후 편집)이 있습니다불변의 일반 쌍 구조체에 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 실제로 이것에 대해 저를 경고) 첫 번째 및 두 번째는 참조 형식되지 않을 수 있지만 코드는 여전히 컴파일합니다. 내가 그들을 비교하기 전에 그들을 (1 차 및 2 차) 물체에 던져 넣어야합니까, 아니면 이것을 할 수있는 더 좋은 방법이 있습니까?

편집 : 가치와 참조 유형을 지원하는이 구조체를 원하는

편집 2 (클래스에 의해 제약, 즉 올바른 해결책이 아니다) : 으로 내가 노력하고있어에 달성하기 위해, 나는 이것이 사전에서 일하기를 바랍니다. 둘째, SRP는 지금 당장이 문제의 본질이 아니기 때문에 중요하지 않습니다. 나중에 언제든지 리팩토링 할 수 있습니다. 세 번째로, default (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

경고와 관련하여 null 대신 default (T) 및 default (K)를 사용할 수 있습니다.

달성하려는 내용을 볼 수 없지만 해시 코드를 사용하여 동일성을 비교해서는 안됩니다. 두 개의 다른 객체가 동일한 해시 코드를 사용한다는 보장은 없습니다. 또한 구조체가 변경할 수없는 경우에도 _first 및 _second 멤버는 변경되지 않습니다.

+0

모든 내장 값 유형을 수행 0 한 디폴트 값의 해시 코드로? – ilitirit

+0

그것이 사실이 아니지만 그것을 보장하기를 원하지 않는다면 나는 놀랄 것입니다. – Joe

+0

또한 기본값 (T) 및 기본값 (K)이 작동하지 않음 "T '및'T '유형에"적용 할 수 없음 == "" – ilitirit

0

우선이 코드는 SRP 원칙을 위반합니다. 항목이 있으면 페어 클래스가 쌍을 유지하는 데 사용됩니다. 기능을 비교하는 동등성을 위임하는 것은 올바르지 않습니다. - 좋은

는 인수 중 하나가 null의 경우 방법이 실패 같음 없습니다 :

는 다음 코드를 살펴 보자. Equals는 Pair 클래스의 해시 코드를 사용하지만 GetHashCode의 정의를 살펴 봅니다. 쌍 멤버 해시 코드의 조합 일뿐입니다. 항목의 평등과는 아무 관련이 없습니다. 나는 Equals 메서드가 실제 데이터를 비교할 것으로 기대한다. 불행히도 올바른 구현을 제공하기에는 너무 바쁩니다. 하지만 처음부터 코드는 틀린 것 같습니다. 성취하고자하는 바를 설명해 주시면 더 좋을 것입니다. 나는 SO 회원들이 너에게 충고 할 수있을 것이라고 확신한다.

+0

먼저 Equals 메서드가 일반 코드 흐름에서 null 매개 변수를 허용 할 수 있습니까? 둘째, 해시 코드 *를 사용하여 동등 함을 나타낼 수 있습니다. 사실, 이것이 가장 자주 사용되는 것입니다 (항목 비교). 이 코드의 유일한 문제점은 First와 Second가 변경되지 않을 수 있다는 것입니다. – ilitirit

+0

사과드립니다. 두 개의 동일한 객체가 동일한 해시 코드를 공유하지만 동일한 해시 코드를 가진 두 개의 객체가 동일하지 않을 수 있습니다. – ilitirit

+0

"불변 쌍"클래스가 불변의 항목 쌍을 보유하고 두 쌍의 해당 항목이 동일 할 경우 쌍 자체도 동일하다는 것을 유지하는 동등 관계를 구현하는 것은 SRP의 위반 사항이 아닙니다. 모든 유형에는 정의 된 등가 관계가 있어야합니다. 변경할 수있는 참조 유형은 기본 참조 동등 관계를 사용해야하며 변경 불가능한 유형 및 값 유형은 해당 필드가 동등한 것으로 간주되는 경우 동등한 것으로 간주해야합니다. – supercat

0

매개 변수로 람다 식을 사용하는 것이 좋습니다. 이렇게하면 내부 일반 유형을 비교하는 방법을 지정할 수 있습니다.

0

이것에 대해 컴파일 할 때 경고 메시지가 나타나지 않지만, 당신은 == null 비교에 대해 이야기하고 있다고 가정합니다. 캐스트가이 모든 것을 다소 깨끗하게 만드는 것처럼 보입니다. 그렇습니다.

추신. 비교 자에 대해 별도의 클래스를 사용해야합니다. 이 클래스는 두 가지 역할 (한 쌍이고 두 쌍을 비교하는 역할)을 채우며 평범하지 않습니다.

1

방법 비교에 해시 코드를 사용하는 경우 해시 코드가 동일하면 "실제 값"을 확인해야합니다.

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"필드의 "Equals"메소드 또는 object.ReferenceEquals를 사용해야합니까? 아니면 더 복잡한 무엇인가?

관련 문제