Effective C# 3rd 読書メモ 20 順序関係はIComparableとIComparerで実装する

独自に定義する型をコレクションがソートしたり検索したりするときのために、順序関係を必要とする場合がある。.NET Frameworkは、順序関係を実装するために2つのインターフェイスを提供している。IComparable<T>IComparer<T>である。

IComparableインターフェイスには、メソッドが1つだけ含まれる。CompareTo()である。比較結果は以下のような戻り値によって表現される。

  • 負の数: 現在のオブジェクト < 比較対象
  • 0: 現在のオブジェクト = 比較対象
  • 1以上: 現在のオブジェクト > 比較対象

新しめの.NET FrameworkのAPIはIComparable<T>を使用するが、古いAPIの中にはIComparableを使うものもある。そのため、IComparable<T>を実装する際は、IComparableも実装すべきである。
以下は、IComparable<T>およびIComparableの実装例。

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

    public Customer(string name)
    {
        _name = name;
    }

    public int CompareTo(Customer other)
    {
        return string.Compare(_name, other._name, StringComparison.Ordinal);
    }

    public int CompareTo(object obj)
    {
        if (!(obj is Customer))
            throw new ArgumentException("Argument is not a customer", nameof(obj));

        var other = (Customer) obj;
        return string.Compare(_name, other._name, StringComparison.Ordinal);
    }
}

さらに、比較の方法自体をカスタマイズしたい場合は、IComparer<T>を使うことができる。

private readonly double _revenue;

private static readonly Lazy<RevenueComparer> RevComp =
    new Lazy<RevenueComparer>(() => new RevenueComparer());

public static IComparer<Customer> RevenueCompare => RevComp.Value;

private class RevenueComparer : IComparer<Customer>
{
    public int Compare(Customer left, Customer right)
    {
        return left._revenue.CompareTo(right._revenue);
    }
}

このようにすれば、Customer.CompareTo()でnameによる比較を提供しつつ、Customer.RevenueCompare.Compare()でrevenueに基づいた比較も提供することができる。

コメントをどうぞ

コメントを残す