IEqualityComparer를 사용하는 방법


97

내 데이터베이스에 같은 번호의 종이 있습니다. 중복없이 모두 얻고 싶습니다. 이 작업을 수행하기 위해 비교 클래스를 만들었지 만 함수를 실행하면 0.6 초에서 3.2 초로 구별없이 함수에서 큰 지연이 발생합니다!

제대로하고 있습니까, 아니면 다른 방법을 사용해야합니까?

reg.AddRange(
    (from a in this.dataContext.reglements
     join b in this.dataContext.Clients on a.Id_client equals b.Id
     where a.date_v <= datefin && a.date_v >= datedeb
     where a.Id_client == b.Id
     orderby a.date_v descending 
     select new Class_reglement
     {
         nom  = b.Nom,
         code = b.code,
         Numf = a.Numf,
     })
    .AsEnumerable()
    .Distinct(new Compare())
    .ToList());

class Compare : IEqualityComparer<Class_reglement>
{
    public bool Equals(Class_reglement x, Class_reglement y)
    {
        if (x.Numf == y.Numf)
        {
            return true;
        }
        else { return false; }
    }
    public int GetHashCode(Class_reglement codeh)
    {
        return 0;
    }
}

16
당신은 한 번 봐 걸릴 수도 있습니다 GetHashCode에 대한 지침과 규칙을
콘래드 Frix에게

이 블로그는 IEqualityComparer를 완벽하게 사용하는 방법을 설명합니다. blog.alex-turok.com/2013/03/c-linq-and-iequalitycomparer.html
Jeremy Ray Brown

답변:


174

귀하의 GetHashCode구현은 항상 같은 값을 반환합니다. Distinct내부적으로 해시 테이블을 구축하기 때문에 효율적인 작업을 위해 좋은 해시 함수에 의존 합니다 .

클래스의 인터페이스를 구현할 때 어떤 계약을 구현해야하는지 아는 문서읽는 것이 중요합니다 . 1

코드에서이 솔루션은 전달하는 것입니다 GetHashCodeClass_reglement.Numf.GetHashCode적절 거기를 구현합니다.

그 외에도 귀하의 Equals방법은 불필요한 코드로 가득 차 있습니다. 다음과 같이 다시 작성할 수 있습니다 (동일한 의미, 코드의 ¼, 더 읽기 쉬움).

public bool Equals(Class_reglement x, Class_reglement y)
{
    return x.Numf == y.Numf;
}

마지막으로, ToList호출은 불필요하고 시간이 많이 소요입니다 : AddRange어떤 받아 IEnumerableA가 너무 변환 List필요하지 않습니다. AsEnumerable되어 에 결과 처리하기 때문에 여기에 중복 AddRange어쨌든이 원인이됩니다.


1 실제로하는 일을 모르고 코드를 작성하는 것을 화물 컬트 프로그래밍 이라고 합니다 . 놀랍도록 널리 퍼진 관행입니다. 근본적으로 작동하지 않습니다.


20
Equals는 x 또는 y가 null이면 실패합니다.
dzendras

4
@dzendras GetHashCode. 그러나의 문서는IEqualityComparer<T>null 인수 로 수행 할 작업을 지정하지 않지만 기사에 제공된 예제도 처리하지 않습니다 null.
Konrad Rudolph

50
와. 혐오감 은 불필요하게 가혹합니다. 우리는 모욕이 아니라 서로를 돕기 위해 여기에 있습니다. 나는 그것이 어떤 사람들을 웃게 만드는 것 같지만 나는 그것을 제거하는 것이 좋습니다.
Jess

5
위키에서 "화물 컬트 프로그래밍"에 대해 읽게 한 다음 스카이프 태그 라인을 "// 여기에서 깊은 마법이 시작됩니다.
Alex

4
@NeilBenn 솔직한 조언을 무례 함으로 착각합니다. OP가 대답을 받아 들였기 때문에 (그리고 훨씬 더 엄격한 버전에서!), 그들은 같은 실수를하지 않는 것 같았습니다. 왜 조언을하는 것이 무례하다고 생각하는지 모르겠지만,“그 남자는 강의가 필요 없다”고 말하면 착각하는 것입니다. 나는 매우 동의하지 않는다. 강의 필요했고, 마음에 새겨졌다. 작성된 코드는 나쁘고 잘못된 작업 관행을 기반으로합니다. 이 점을 지적하지 않는 것은 해를 입히고 전혀 도움이되지 않을 것입니다. 그 이후로 OP는 작동 방식을 개선 할 수 없었습니다.
콘라드 루돌프

47

이 코드를 시도하십시오.

public class GenericCompare<T> : IEqualityComparer<T> where T : class
{
    private Func<T, object> _expr { get; set; }
    public GenericCompare(Func<T, object> expr)
    {
        this._expr = expr;
    }
    public bool Equals(T x, T y)
    {
        var first = _expr.Invoke(x);
        var sec = _expr.Invoke(y);
        if (first != null && first.Equals(sec))
            return true;
        else
            return false;
    }
    public int GetHashCode(T obj)
    {
        return obj.GetHashCode();
    }
}

그 사용의 예는

collection = collection
    .Except(ExistedDataEles, new GenericCompare<DataEle>(x=>x.Id))
    .ToList(); 

19
GetHashCode표현식도 사용해야 합니다. 사용법은 이 게시물return _expr.Invoke(obj).GetHashCode();참조하십시오 .
orad 2014-08-26

3

구현 GetHashCodeNULL유효성 검사를 통해 코드 만 작성하십시오 .

public class Class_reglementComparer : IEqualityComparer<Class_reglement>
{
    public bool Equals(Class_reglement x, Class_reglement y)
    {
        if (x is null || y is null))
            return false;

        return x.Numf == y.Numf;
    }

    public int GetHashCode(Class_reglement product)
    {
        //Check whether the object is null 
        if (product is null) return 0;

        //Get hash code for the Numf field if it is not null. 
        int hashNumf = product.hashNumf == null ? 0 : product.hashNumf.GetHashCode();

        return hashNumf;
    }
}

예 : Numf에 의해 구별되는 Class_reglement 목록

List<Class_reglement> items = items.Distinct(new Class_reglementComparer());

2

비교 클래스 (또는 더 구체적 AsEnumerable으로 작동하기 위해 사용해야 하는 호출)를 포함한다는 것은 정렬 논리가 데이터베이스 서버 기반에서 데이터베이스 클라이언트 (응용 프로그램)에 있다는 것을 의미합니다. 즉, 클라이언트는 이제 더 많은 수의 레코드를 검색 한 다음 처리해야하므로 적절한 인덱스를 사용할 수있는 데이터베이스에서 조회를 수행하는 것보다 항상 효율성이 떨어집니다.

대신 요구 사항을 충족하는 where 절을 개발해야합니다 . 자세한 내용 은 LINQ to Entities Except 절과 함께 IEqualityComparer 사용 을 참조하세요.


2

권투없이 일반적인 솔루션을 원하는 경우 :

public class KeyBasedEqualityComparer<T, TKey> : IEqualityComparer<T>
{
    private readonly Func<T, TKey> _keyGetter;

    public KeyBasedEqualityComparer(Func<T, TKey> keyGetter)
    {
        _keyGetter = keyGetter;
    }

    public bool Equals(T x, T y)
    {
        return EqualityComparer<TKey>.Default.Equals(_keyGetter(x), _keyGetter(y));
    }

    public int GetHashCode(T obj)
    {
        TKey key = _keyGetter(obj);

        return key == null ? 0 : key.GetHashCode();
    }
}

public static class KeyBasedEqualityComparer<T>
{
    public static KeyBasedEqualityComparer<T, TKey> Create<TKey>(Func<T, TKey> keyGetter)
    {
        return new KeyBasedEqualityComparer<T, TKey>(keyGetter);
    }
}

용법:

KeyBasedEqualityComparer<Class_reglement>.Create(x => x.Numf)

0

IEquatable<T> 최신 프레임 워크를 사용하면이 작업을 훨씬 쉽게 수행 할 수 있습니다.

멋진 간단한 bool Equals(T other)함수를 얻을 수 있으며 캐스팅하거나 별도의 클래스를 만드는 데 방해가 되지 않습니다.

public class Person : IEquatable<Person>
{
    public Person(string name, string hometown)
    {
        this.Name = name;
        this.Hometown = hometown;
    }

    public string Name { get; set; }
    public string Hometown { get; set; }

    // can't get much simpler than this!
    public bool Equals(Person other)
    {
        return this.Name == other.Name && this.Hometown == other.Hometown;
    }

    public override int GetHashCode()
    {
        return Name.GetHashCode();  // see other links for hashcode guidance 
    }
}

GetHashCode이것을 사전에서 사용 하는 경우 또는 Distinct.

추신. 사용자 지정 Equals 메서드가 데이터베이스 측에서 직접 엔터티 프레임 워크와 작동하지 않는다고 생각합니다 (AsEnumerable을 수행하기 때문에 이것을 알고 있다고 생각합니다). 이것은 일반적인 경우에 대해 간단한 Equals를 수행하는 훨씬 간단한 방법입니다.

일이 작동하지 않는 것 같으면 (예 : ToDictionary를 수행 할 때 중복 키 오류) Equals 안에 중단 점을 넣어 그것이 적중되고 있는지 확인하고 GetHashCode(override 키워드로) 정의 했는지 확인하십시오 .


1
당신은 여전히 널 (null)를 확인하기 위해 필요
disklosr

나는 그것을 만난 적이 없지만 다음에 그렇게 할 것을 기억할 것입니다. List <T> 또는 이와 유사한 것에 null이 있습니까?
Simon_Weaver

1
언더 .Equals()방법 당신은 비교가 나타납니다 other.Hometown대신 자체에this.Hometown
제이크 스톡스

죄송합니다. 고정 오타 :)
Simon_Weaver
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.