2014-04-08 3 views
-1

클래스 B에서 상속받은 클래스 A가 있는데 IEqualityComparer<A>을 구현합니다. 이것은 클래스 A가 Equals와 GetHashCode 메소드의 구현을 제공한다는 것을 의미합니다. 여태까지는 그런대로 잘됐다. 문제는 코드는 다음과 같은 방법으로 동작하는 이유를 이해하지 못하는 것입니다 : A의를 GetHashCode 구현 "OBJ"매개 변수 인 상태 obj.GetHashCode()this.GetHashCode() 대신 반환하는 경우IEqualityComparer 구현에서 GetHashCode와 Equals 사이의 관계는 무엇입니까?

만의에 도달 할 디버거가 실행 중단 점을 같음 GetHashCode의 서명이 정의하는 (내 경우에는 A 유형의 변수).

직관적으로 나는받은 객체의 해시 코드를 반환해야한다고 생각했지만 컴파일러가 인스턴스의 Equals 구현을 무시하게했습니다.

왜 이런 일이 발생합니까?

코드 데모 :

public class A : B, IEqualityComparer<A> 
{ 
    public bool Equals(A x, A y) 
    { 
     //my implementation... 
    } 

    public int GetHashCode(A obj) 
    { 
     //return obj.GetHashCode(); -> this makes my Equals implementation above be ignored! Why? 
     return this.GetHashCode(); -> my Equals implementation is used 
    } 
} 
+0

문제를 설명하는 코드를 게시 할 수 있습니까? 맞 춥니 다. –

답변

1

IEqualityComparer<T> 구현은 overrideGetHashCodeEquals의 기본 구현을하지 않습니다.

IEqualityComparer<T>을 구현하면 구현 자 인스턴스를 T의 동등 비교 자로 제공 할 수 있습니다. 이것은 여러 linq 확장 및 일반 컬렉션 생성자에 공통적 인 매개 변수입니다.

EqualsGetHashCode을 대체하면 클래스 인스턴스의 평등성이 테스트됩니다. 대체 IEqualityComparer<T>을 제공하지 않는 =!= 연산자 및 linq 확장과 일반 콜렉션 생성자와 같은 EqualsGetHashCode을 호출하는 다른 임 플리 멘 테이션을 활용하십시오.

이러한 개념은 비슷하지만 은 다른 용도로 사용되지만 부분적으로 바꿀 수는 없습니다.


public class A 
{ 
    public string Value1 { get; set; } 
    public int Value2 { get; set; } 

    public override int GetHashCode() 
    { 
     unchecked 
     { 
      int hash = 17; 
      hash = (hash * 23) + 
       StringComparer.Ordinal.GetHashCode(this.Value1); 
      hash = (hash * 23) + this.Value2; 
      return hash; 
     } 
    } 

    public override bool Equals(object obj) 
    { 
     var a = obj as A; 
     if (a == null) 
     { 
      return false; 
     } 

     if (a.Value2 != this.Value2) 
     { 
      return false; 
     } 

     return StringComparer.Ordinal.Equals(
      a.Value1, 
      this.Value1); 
    } 
} 
A의이 구현이 제대로 EqualsGetHashCode 우선

,이 변화는 LINQ 확장을 호출 한 후 다음을 보장하기에 충분하다, 내가 예와 확장하자

var distinct = aSequneceOfA.Distinct(); 

distinct에는 모두 동일한 Value2을 가진 인스턴스가 포함되지 않습니다. 일반적으로는 Value1입니다. 이것을 달성하기 위해 다른 인터페이스 구현이 필요하지 않습니다.


자, 아마도 내가 어떤 소문자 구분을 필요로, 어떤 상황에서 내가 Value1이 서수 비교와 행복하지 않았다 것으로 가정합니다. 새로운 평등 비교자를 구현할 수도 있습니다.이 날 Distinct의 다른 오버로드를 호출 할 수 것

public class AComparerInsensitive : IEqualityComparer<A> 
{ 
    public bool Equals(A x, A y) 
    { 
     if (x == null) 
     { 
      return y == null; 
     } 

     if (y == null) 
     { 
      return false; 
     } 

     if (x.Value2 != y.Value2) 
     { 
      return false; 
     } 

     return StringComparer.CurrentCultureIgnoreCase.Equals(
      x.Value1, 
      y.Value1) 
    } 

    public int GetHashCode(A a) 
    { 
     if (a == null) 
     { 
      return 0; 
     } 

     unchecked 
     { 
      int hash = 17; 
      hash = (hash * 23) + 
       StringComparer.CurrentCultureIgnoreCase.GetHashCode(
        a.Value1); 
      hash = (hash * 23) + a.Value2; 
      return hash; 
     } 
    } 
} 

,

var insensitivelyDistinct = aSequneceOfA.Distinct(
    new AComparerInsensitive()); 

AEqualsGetHashCode을 무시하고 비교를 수행 할 AComparerInsensitive를 사용하여 별개의 ingnores이 과부하.

+0

@Veverke 확장 된 대답을 통해 문제가 명확 해집니다. – Jodrell

1

잘못된 인터페이스를 사용하고있는 것 같습니다. IEqualityComparer<>은 일반적으로 기타 유형을 비교하는 클래스에 사용됩니다.

당신의 유형은 단순히 IEquatable<A> 재정 Equals(object)GetHashCode()를 구현해야합니다. 서명에 유의하십시오. 이처럼

:

public class A : B, IEquatable<A> 
{ 
    public bool Equals(A other) 
    { 
     if (other == null || GetType() != other.GetType()) 
      return false; 

     //your implementation 
    } 

    public override bool Equals(object obj) 
    { 
     return Equals(obj as A); 
    } 

    public override int GetHashCode() 
    { 
     //your implementation 
    } 
} 

그런 다음 당신이 someEnumerableOfA.Distinct() 같은 물건을 할 수 있고, Linq는 방법은 구현을 사용합니다.

public class A : B // no interfaces 
{ 
} 

public class AEqualComparer : IEqualityComparer<A> 
{ 
    public bool Equals(A x, A y) 
    { 
     //your implementation 
    } 

    public int GetHashCode(A x) 
    { 
     //your implementation 
    } 
} 

당신이 someEnumerableOfA.Distinct(new AEqualComparer()) 필요이 다른 옵션 :

다른 옵션은하는 것입니다.

+0

IEqualityComparer를 선택한 이유는 내 솔루션이 IEnumerable <>. Distinct <> (IEqualityComparer <>) 호출에 의존하기 때문입니다. 즉, 내 컬렉션의 고유 한 요소를 반환하려는 경우 여기에서 Distinct를 사용할 수 없으며 따라서 IEqualityComparer 구현을 제공해야합니까? – Veverke

+0

OP는 올바른 인터페이스를 구현하지만 잘못된 방식으로 구현됩니다. 자세한 정보는 링크 된 복제 스레드를 참조하십시오. –

+0

@Veverke,'Equals'와'GetHashCode'를 오버라이드하면'IEqualityComparer '을 구현하거나 제공 할 필요가 없습니다. – Jodrell

관련 문제