[Effective C#] 아이템 20: IComparable와 IComparer를 이용하여 객체의 선후 관계를 정의하라


IComparable

IComparable 에는 CompareTo()라는 단 하나의 메서드가 정의되어 있다.

CompareTo()는 현재 객체가 대상 객체보다 작으면 0보다 작은 값, 같으면 0, 크면 0보다 큰 값을 반환한다.

public struct Customer : IComparable<Customer>, IComparable
{
    private readonly string name;

    public Customer(string name)
    {
        this.name = name;
    }

    // IComparable<Customer> 멤버
    public int CompareTo(Customer other) => name.CompareTo(other.name);
}

(제네릭이 아닌) IComparable

IComparable 를 구현할 때에는 하위호환을 위해 IComparable 도 함께 구현해야 한다.

	// IComparable 멤버
    int IComparable.CompareTo(object obj)
    {
        if (!(obj is Customer))
            throw new ArgumentException("Argument is not a Customer", "obj");

        Customer otherCustomer = (Customer)obj;

        return this.CompareTo(otherCustomer);
    }

IComparable 은 박싱/언박싱이 필요하므로 상당한 성능 비용이 필요하다.

IComparable.CompareTo()를 호출 하기 위해서는 IComparable로 명시적인 형변환이 필요하다.

관계 연산자

표준 관계 연산자를 오버로딩하여 CompareTo() 메서드를 사용하도록 할 수 있다.

	// 관계 연산자
    public static bool operator < (Customer left, Customer right) =>
        left.CompareTo(right) < 0;
    public static bool operator <= (Customer left, Customer right) =>
        left.CompareTo(right) <= 0;
    public static bool operator > (Customer left, Customer right) =>
        left.CompareTo(right) > 0;
    public static bool operator >= (Customer left, Customer right) =>
        left.CompareTo(right) >= 0;

Comparison

타입에 관한 기본적인 선후 관계 이외에 추가적인  기준으로 정렬이 필요할 경우 Comparison를 사용할 수 있다.

	public static Comparison<Customer> CompareByRevenue =>
        (left, right) => left.revenue.CompareTo(right.revenue);

IComparer

IComparer를 사용해서 추가적인 정렬 기준을 만들 수도 있다. (제네릭이 생기기 전부터 IComparer가 사용되었었다)

	private class RevenueComparer : IComparer<Customer>
    {
        // IComparer<Customer> 멤버
        int IComparer<Customer>.Compare(Customer left, Customer right) =>
            left.revenue.CompareTo(right.revenue);
    }

    private static Lazy<RevenueComparer> revComp =
        new Lazy<RevenueComparer>(() => new RevenueComparer());
    
    public static IComparer<Customer> RevenueCompare => revComp.Value;

Reference

  • [Comparison 대리자](https://docs.microsoft.com/ko-kr/dotnet/api/system.comparison-1?view=net-6.0){:target="_blank"}
  • [IComparer Interface](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.icomparer-1?view=net-6.0){:target="_blank"}
  • C#의 역사
  • 닷넷 프레임워크 버전 역사
  • [Lazy 클래스](https://docs.microsoft.com/ko-kr/dotnet/api/system.lazy-1?view=net-6.0){:target="_blank"}

댓글남기기