2011-09-04 15 views
29

IComparableと汎用IComparable<T>の両方を実装する必要がありますか?私がそれらのうちの1つだけを実装すると、何らかの制限がありますか?IComparableとIComparable <T>

+0

あなたは本当に 'IComparable 'のコードを書く必要があります。実際の比較をジェネリック実装に委譲することによって、無料でIComparableを取得します。 –

答えて

18

はい、両方を実装する必要があります。

1つを実装すると、他のコードに依存するコードはすべて失敗します。

IComparableまたはIComparable<T>のいずれかを使用するコードがたくさんありますが、両方を実装するとコードが確実にそのようなコードで動作するようになります。

+0

すべてのコードを管理している場合は、汎用バージョンのみを実装する必要があると判断できますか?あるいは、フレームワークコードのいくつかはまだジェネリックでないバージョンを必要としますか? –

+0

@Davidでは、一部のフレームワーククラス(コレクションなど)は、どちらか一方に依存することがあります。 –

+5

@David - 「IComparable」は確かにいくつかのフレームワークコードで使用されています。私は 'Array.Sort'と' ArrayList.Sort'がそれを使うと信じています。 – Oded

4

IEquatable <T>は一般に、実装が単純にObject.Equalsを呼び出さない限り、そのような派生が継承に奇妙に再生されるため、開封されていないクラスによって実装されるべきではありません(その場合は意味がありません)。ジェネリックIComparable <T>。 Object.EqualsとIEquatableのセマンティクスは<T>の意味は、IEquatable <T>が定義されている場合、その動作はObject.Equalsの動作を反映している必要があることを意味します。 DerivedFoo型とみなされるDerivedFoo型の2つのオブジェクトは、型がDerivedFooであると見なされたときに等しいと比較され、Foo型のオブジェクトと見なされるときにequalも比較する必要があります。一方、DerivedFoo型の2つのオブジェクトは、型DerivedFooとみなされたときに不等にランク付けされるオブジェクトは、Foo型とみなされると等しくランク付けされる可能性があります。これを保証する唯一の方法は、IComparable <T>を使用することです。

たとえば、(DateTime型の)ScheduledTimeフィールドと(MethodInvokerタイプの)ScheduledActionフィールドを含むSchedulerEventクラスがあるとします。このクラスには、サブタイプSchedulerEventWithMessage(文字列型のMessageフィールドを追加する)とSchedulerEventWithGong(Double型のGongVolumeフィールドを追加する)が含まれます。 SchedulerEventクラスはScheduledTimeによって自然順序付けされていますが、順序の異なるイベントは不等である可能性があります。 SchedulerEventWithMessageおよびSchedulerEventWithGongクラスも、それらの間で自然順序付けを持ちますが、SchedulerEventクラスの項目と比較して自然順序はありません。

2つのSchedulerEventWithMessageイベントXとYが同じ時刻にスケジュールされているとしますが、X.Messageが "aardvark"で、Y.Messageが "zymurgy"であるとします。 (IComparable <スケジューライベント>)X).CompareTo(Y)は、イベントが等しい時刻を持つのでゼロを報告するが、(IComparable <SchedulerEventWithMessage>)X).CompareTo(Y)は負の数を返すべきである( "aardvark" "zymurgy"の前の並べ替え)。クラスがそのように動作しなかった場合、SchedulerEventWithMessageオブジェクトとSchedulerEventWithGongオブジェクトが混在したリストを一貫して並べることは困難または不可能です。

ちなみに、IEquatableのセマンティクスを持つことは有用であると主張するかもしれません。<T>オブジェクトはタイプTのメンバーの基底だけを比較します。 IEquatable <SchedulerEvent>は、ScheduledTimeとScheduledActionが等しいかどうかをチェックしますが、SchedulerEventWithMessageまたはSchedulerEventWithGongに適用しても、MessageプロパティまたはGongVolumeプロパティはチェックされません。確かに、これらはIEquatableの便利なセマンティクスとなるでしょう。メソッドの意味がありますが、そのようなセマンティクスを好むでしょうが、1つの問題のために:Comparer <T> .Default.GetHashCode(T)は、常に同じ関数Object.GetHashCode()これは、IEquatable <T>が異なるタイプTの動作を変更する能力を大幅に制限します。

+0

非常に興味深い答え。 –

19

Odedは、実装の1つのみに依存するコレクションやその他のクラスがあるため、両方を実装する必要があります。

しかし、そこにトリックがあります:IComparable <T>は例外をスローするべきではありませんが、IComparableは必要です。 IComparable <T>を実装するときは、Tのすべてのインスタンスを互いに比較できるようにする責任があります。これにはnullも含まれます(nullをTのすべてのnull以外のインスタンスよりも小さく扱うとうまくいく)。

一般的なIComparableはSystem.Objectを受け入れます。考えられるすべてのオブジェクトがTのインスタンスと同等であることを保証することはできません。したがって、T以外のインスタンスがIComparableに渡された場合は、単にSystem.ArgumentException 。それ以外の場合は、コールをIComparable <T>の実装にルーティングします。ここで

は一例です:

public class Piano : IComparable<Piano>, IComparable 
{ 
    public int CompareTo(Piano other) { ... } 
    ... 
    public int CompareTo(object obj) 
    { 

     if (obj != null && !(obj is Piano)) 
      throw new ArgumentException("Object must be of type Piano."); 

     return CompareTo(obj as Piano); 

    } 
} 

この例では、IComparableを<T>を実装する際の世話をする必要があること、副作用の広範な分析が含まれてはるかに長い記事の一部です:How to Implement IComparable<T> Interface in Base and Derived Classes