2010-03-21 3 views
17

이 질문에는 본질적으로 동일한 코드의 두 가지 구현이 포함됩니다.오브젝트 비교를위한 다른 구현의 장점/단점

첫째, 개체의 컬렉션 정렬 할 때 매개 변수로 사용할 수있는 비교 방법을 만들 대리자를 사용 : 나는의 컬렉션을 정렬하는 방법을하고자 할 때

class Foo 
{ 
    public static Comparison<Foo> BarComparison = delegate(Foo foo1, Foo foo2) 
    { 
     return foo1.Bar.CompareTo(foo2.Bar); 
    }; 
} 

내가 위의 사용을 Foo 객체는 CompareTo 함수와 다른 방식으로 제공됩니다.

List<Foo> fooList = new List<Foo>(); 
fooList.Sort(BarComparison); 

둘째, IComparer를 사용하여 : : 예를 들어 내가 푸 개체의 컬렉션에 푸 개체에 대한 이진 검색을 수행 할 때

public class BarComparer : IComparer<Foo> 
{ 
    public int Compare(Foo foo1, Foo foo2) 
    { 
     return foo1.Bar.CompareTo(foo2.Bar); 
    } 
} 

나는 위를 사용합니다. 예를 들어 :

BarComparer comparer = new BarComparer(); 
List<Foo> fooList = new List<Foo>(); 
Foo foo = new Foo(); 
int index = fooList.BinarySearch(foo, comparer); 

내 질문

은 이러한 구현의 각각의 장점과 단점은 무엇

  • 입니까?
  • 이러한 구현을 활용하는 방법에는 어떤 것이 있습니까?
  • 코드를 복제 할 필요가없는 방식으로 이러한 구현을 결합하는 방법이 있습니까?
  • 이 구현 중 단 하나만 사용하여 이진 검색과 대체 수집 정렬을 모두 수행 할 수 있습니까?

답변

5

Comparison<T>IComparer<T>과 반대되는 것으로 받아들이는 데 가장 큰 이점은 아마도 익명의 방법을 쓸 수 있다는 것입니다. 내가 가지고있는 경우, MyClass 정렬에 사용되어야한다 ID 속성을 포함하는 List<MyClass>가, 내가 쓸 수의 말을하자 : 전체 IComparer<MyClass> 구현을 작성하는 것보다 훨씬 더 편리

myList.Sort((c1, c2) => c1.ID.CompareTo(c2.ID)); 

합니다.

IComparer<T>을 수락하는 것이 레거시 코드 (.NET Framework 클래스 포함)와의 호환성을 제외한 모든 주요 이점을 갖고 있는지 확실하지 않습니다.Comparer<T>.Default 속성은 기본 유형에만 유용합니다. 그 외 모든 것은 대개 코드 작업에 추가 작업이 필요합니다.

내가 일반적으로 할 한 가지가 다음과 같이 일반적인 비교자를 만드는 것입니다, IComparer<T> 작업을 필요로 할 때 코드 중복을 방지하려면 :

myList.BinarySearch(item, 
    new AnonymousComparer<MyClass>(x.ID.CompareTo(y.ID))); 
:

public class AnonymousComparer<T> : IComparer<T> 
{ 
    private Comparison<T> comparison; 

    public AnonymousComparer(Comparison<T> comparison) 
    { 
     if (comparison == null) 
      throw new ArgumentNullException("comparison"); 
     this.comparison = comparison; 
    } 

    public int Compare(T x, T y) 
    { 
     return comparison(x, y); 
    } 
} 

이 같은 코드를 작성 할 수 있습니다

정확히는 아니지만 다소 시간을 절약 할 수 있습니다.

내가 가진 또 다른 유용한 클래스입니다이 하나

public class PropertyComparer<T, TProp> : IComparer<T> 
    where TProp : IComparable 
{ 
    private Func<T, TProp> func; 

    public PropertyComparer(Func<T, TProp> func) 
    { 
     if (func == null) 
      throw new ArgumentNullException("func"); 
     this.func = func; 
    } 

    public int Compare(T x, T y) 
    { 
     TProp px = func(x); 
     TProp py = func(y); 
     return px.CompareTo(py); 
    } 
} 

당신은뿐만 IComparer<T>을 위해 설계 코드를 작성할 수 있습니다 :

myList.BinarySearch(item, new PropertyComparer<MyClass, int>(c => c.ID)); 
+0

훌륭한 코드 예제! –

7

성능 측면에서 두 옵션 중 어떤 것도 이점이 없습니다. 실제로는 편리함과 코드 유지 관리의 문제입니다. 원하는 옵션을 선택하십시오. 즉, 문제의 방법은 선택 사항을 약간 제한합니다.

List<T>.SortIComparer<T> 인터페이스를 사용하면 코드를 중복하지 않아도됩니다.

불행히도 BinarySearchComparison<T>을 사용하는 옵션을 구현하지 않으므로 해당 방법에 대해 Comparison<T> 대리인을 사용할 수 없습니다. 적어도 직접적으로는 사용하지 마십시오.

당신이 정말로 모두 Comparison<T>을 사용하고자하는 경우, 당신은 생성자에 Comparison<T> 대리자를 갔고, IComparer<T>을 구현하는 일반 IComparer<T> 구현을 만들 수 있습니다. 귀하의 경우에는

public class ComparisonComparer<T> : IComparer<T> 
{ 
    private Comparison<T> method; 
    public ComparisonComparer(Comparison<T> comparison) 
    { 
     this.method = comparison; 
    } 

    public int Compare(T arg1, T arg2) 
    { 
     return method(arg1, arg2); 
    } 
} 
0

IComparer<T>Comparision<T> 이상 대리자를 갖는 이점은 당신이 전혀 Comparison 위임 버전을 필요가 없습니다 당신은 또한, 정렬 방법을 위해 사용할 수 있다는 것입니다.

public class DelegatedComparer<T> : IComparer<T> 
{ 
    Func<T,T,int> _comparision; 
    public DelegatedComparer(Func<T,T,int> comparision) 
    { 
    _comparision = comparision; 
    } 
    public int Compare(T a,T b) { return _comparision(a,b); } 
} 

list.Sort(new DelegatedComparer<Foo>((foo1,foo2)=>foo1.Bar.CompareTo(foo2.Bar)); 

하고보다 고급 버전 :

public class PropertyDelegatorComparer<TSource,TProjected> : DelegatedComparer<TSource> 
{ 
    PropertyDelegatorComparer(Func<TSource,TProjected> projection) 
    : base((a,b)=>projection(a).CompareTo(projection(b))) 
} 
+0

오타 : 라인 8에서 닫는 중괄호를'}'실종 첫 번째 코드 스 니펫. –

1

위임 기술은 매우 짧은 (람다 표현식 수 있습니다

당신이 할 수있는 또 다른 유용한 것은 위임 IComparer<T>이 같은 구현을 구현하고 심지어 짧은 경우), 더 짧은 코드가 목표라면, 이것은 이점입니다.

그러나 IComparer (및 이에 해당하는 해당 항목)를 구현하면 코드가 더 쉽게 테스트 할 수 있습니다. 클래스/메서드 비교에 일부 단위 테스트를 추가 할 수 있습니다.

또한 두 개 이상의 비교자를 작성하고이를 새 비교 자로 결합 할 때 비교 자 구현을 다시 사용할 수 있습니다. 익명의 대표자와의 코드 재사용은 달성하기가 더 어렵습니다.

그래서, 그것을 요약하면 :

익명 대표 : 짧은 (그리고 아마도 청소기) 코드

명시 구현 : 테스트 용이성과 코드 재사용.

+1

코드 재사용에 관해서는 동의하지만 테스트 가능성에 대해서는 확신하지 못했습니다. 'IComparer '을 받아들이는 메소드가'Comparison '을 받아들이는 것보다 테스트하기 쉬운 이유는 무엇입니까? 둘 다 통제의 반전을 사용합니다. – Aaronaught

+1

@Aaronaught, 나는 오해의 소지가 있다고 생각한다. ** 테스트하기가 더 어려운 익명의 델리게이트와는 달리 ** 명백한 구현은 테스트하기 쉽다 (IComparer 와 비교는 ). –

+0

아, 이해 하겠지만, IComparer 자체를 단위 테스트하는 것이지 허용하는 방법이 아닙니다. 실제로 그 중 하나를 테스트하고 싶다고 상상할 수는 없지만 원하는 경우 반대하는 테스트를 작성하는 것이 훨씬 쉽습니다. – Aaronaught

0

그들은 정말 다양한 요구를 충족 :

IComparable 주문 된 개체에 유용합니다. 실수는 비교 가능해야하지만 복소수는 잘못 정의 할 수 없습니다. 잘못 정의 할 수는 없습니다.

IComparer은 재사용 가능한 잘 캡슐화 된 비교자를 정의 할 수 있습니다. 이는 비교가 몇 가지 추가 정보를 알아야 할 경우 특히 유용합니다. 예를 들어, 다른 시간대의 날짜와 시간을 비교할 수 있습니다. 이는 복잡 할 수 있으며이 목적을 위해 별도의 비교자를 사용해야합니다.

비교 방법은 모든 재사용 가능성을 위해 충분히 복잡하지 않은 간단한 비교 연산을 위해 만들어진다. 첫 번째 이름으로 고객 목록 정렬. 이것은 간단한 조작이므로 추가 데이터가 필요하지 않습니다. 마찬가지로 객체가 자연스럽게 정렬되지 않기 때문에 객체에 고유하지 않습니다.

마지막으로,는 3 개의 객체가 같은지 아닌지를 결정할 수있는 경우에만 중요 할 수 있지만 '큰'과 '작은'의 개념이없는 경우 중요 할 수 있습니다. 복소수 또는 공간상의 벡터.