2009-07-21 2 views
3

デフォルトのコンストラクタを使用してステートフルIComparer<T>を書き込んだことはありません。 Reflectorでチェックしたすべての標準ライブラリの実装は、ステートレスでもあります。したがって、私は自由にこのようなIComparer<T>キャッシュできることを前提としたいと思います。だからここステートフルIComparer <T>の妥当なシナリオはありますか?

PriorityQueue<TPriority, TComparer> where TComparer : IComparer<TPriority>, new() 
{ 
    private static TComparer _comparer = new TComparer(); 

    public PriorityQueue() {...} 
    ... 
} 

代わりの

PriorityQueue<TPriority> 
{ 
    private IComparer<TPriority> _comparer; 

    public PriorityQueue(IComparer<TPriority> comparer) { 
     _comparer = comparer; 
     ... 
    } 

    ... 
} 

を質問です:あなたは今まで書かれている/このためIComparer<T>を見て故障するでしょうか?はいの場合、どれくらい一般的ですか?

編集:私は本当にこの場合の2番目のバージョンのオーバーヘッドを望まない理由は、データ構造が永続的であるということです。ノードが親/ルート参照を持たないツリーとして実装されています。だから、キューごとの比較者への参照ではなく、ノードごとの比較者への参照です。私のオリジナルデザインはIComparable<T>を使用し、カスタム比較のためのラッパー構造体を書くことを推奨しました。

答えて

3

まあ、スタティック comparerを使用すると、異なるキューで異なる比較を行うことはできません。これは問題になることがあります...時折人々を行うカスタム比較が必要です。たとえば、型を制御していない場合などです。私のデフォルトのアプローチは、

PriorityQueue<TPriority> 
{ 
    private IComparer<TPriority> _comparer; 

    public PriorityQueue(IComparer<TPriority> comparer) { 
     _comparer = comparer; 
     ... 
    } 

    public PriorityQueue() : this(Comparer<T>.Default) {} 
} 

です。はい、私はいくつか書いた - 特にたとえば... LINQスタイルの投影する比較器を書き込むため、何かのように:

IComparer<Customer> comparer = ProjectionComparer<Customer> 
      .CompareBy(cust => cust.Name); 

インスタンス「ソートを名前で」比較:できます

public static class ProjectionComparer<TSource> 
{ 
    public static IComparer<TSource> CompareBy<TValue>(
     Func<TSource, TValue> selector) 
    { 
     return CompareBy<TValue>(selector, Comparer<TValue>.Default); 
    } 
    public static IComparer<TSource> CompareBy<TValue>(
     Func<TSource, TValue> selector, 
     IComparer<TValue> comparer) 
    { 
     return new ProjectionComparerItem<TValue>(
      selector, Comparer<TValue>.Default); 
    } 
    class ProjectionComparerItem<TValue> : IComparer<TSource> 
    { 
     private readonly IComparer<TValue> comparer; 
     private readonly Func<TSource, TValue> selector; 
     public ProjectionComparerItem(
      Func<TSource, TValue> selector, 
      IComparer<TValue> comparer) 
     { 
      this.selector = selector; 
      this.comparer = comparer; 
     } 
     public int Compare(TSource x, TSource y) 
     { 
      // TODO: some null stuff... 
      return comparer.Compare(selector(x), selector(y)); 
     } 
    } 
} 

。私は、少なくともいくつかのケースのためにそれで行くことが

private class PriorityQueueImpl<TPriority, TComparer> where TComparer : IComparer<TPriority> { 
    // all methods accept a TComparer 
    // generic in TComparer to avoid boxing for struct TComparers and permit inlining for sealed TComparers 
} 

public struct PriorityQueue<TPriority, TComparer> where TComparer : IComparer<TPriority> { 
    private readonly PriorityQueueImpl<TPriority, TComparer> _impl; 
    private readonly TComparer _comparer; 

    // methods delegate to _impl 
} 

+0

これは良い例ですが、デフォルトのコンストラクタがないため、静的に誤って使用することはできませんでした。 –

+0

'TComparer 'によるパラメータ設定のポイントは、異なる比較を可能にすることです。異なる比較を持つキューには異なるタイプがありますが、異なる比較を持つ(たとえば)キューをマージすることは意味がありません。 –

+0

そして、私の指摘は、比較ごとに常に型が必要なわけではないということです。上記のクラスは、同じ型とのさまざまな比較を可能にします。なぜ人々はクラスを書くように強制するのですか? –

1

はい、私は持っていますが、それはかなり珍しいと思います。

まれに、他のデータに依存する比較を実装することがあります。たとえば、IComparerの一部として比較に使用される軸を供給する、いくつかの空間ルーチンがあります。

言われているように、別の比較クラスを使用するだけでこれを回避するのはかなり簡単です。これは多分もっと良い設計です。あなたは存在する必要があるIComparer<T>実装に制限を設けていますので、あなたの根拠を文書化します。

個人的には、IComparer<T>を非静的にし、2つのコンストラクタ(外部インスタンスを取るコンストラクタとデフォルトのコンペアラを作成するコンストラクタ)を用意します。あなたは、キューごとに比較者の余分なオーバーヘッドを持っていますが、それは非常に最小です(状態がない場合はほぼ0です。これは "空の"オブジェクトへの単一のオブジェクト参照なので)。

+0

私は同意しますが、オーバーヘッドはそれよりも大きいと思われます。編集を参照してください。 –

+0

コンストラクタが要素のない構造体の場合、オーバーヘッドはほとんどありません...私はおそらくおそらくおそらくそれの柔軟性を考えると、キューごとに1つのアプローチをしています。 –

+0

コンストラクタが構造体の場合は、実際にそれをジェネリックにする必要があります。あなたが 'IComparer 'としてそれを使用すれば、あなたは毎回ボクシングオーバーヘッドを得るでしょう! –

0

もう一つの可能​​なアプローチ。

関連する問題