3

IComparer<T>デフォルトのコンストラクターを使用してステートフルを作成したことはありません。Reflector で確認したすべての標準ライブラリの実装もステートレスです。したがって、次のように自由にキャッシュできると想定したいと思い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番目のバージョンのオーバーヘッドが本当に欲しくない理由は、データ構造が永続的であるためです。ノードが親/ルート参照を持たないツリーとして実装されます。したがって、キューごとに比較子への 1 つの参照ではなく、ノードごとに比較子への 1 つの参照になります! 私の当初の設計はIComparable<T>、カスタム比較用のラッパー構造体を使用して作成することをお勧めするだけでした。

4

3 に答える 3

3

静的比較子があるということは、異なるキューで異なる比較を行うことができないということです。これは問題になる可能性があります...カスタム比較が必要になる場合がありますたとえば、型を制御しない場合です。私のデフォルトのアプローチは次のとおりです。

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

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

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

ステートフルな比較子について。はい、私はいくつか書きました-特にLINQスタイルの射影比較子を書くために...たとえば、次のようなものです:

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));
        }
    }
}

これにより、次のことが可能になります。

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

インスタンスの「名前による並べ替え」比較。

于 2009-07-21T21:25:12.233 に答える
1

はい、ありますが、かなり珍しいと思います。

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

そうは言っても、別の比較クラスを使用するだけでこれを回避するのは非常に簡単であり、おそらく多くの点で優れた設計です。IComparer<T>ただし、存在する必要がある実装に制限を加えているので、その根拠を文書化します。

私の個人的な好みは、IComparer<T>非静的にし、2 つのコンストラクターを提供することです。1 つは外部インスタンスを受け取り、もう 1 つは既定の比較子を作成します。キューごとに比較子の余分なオーバーヘッドがありますが、それはごくわずかです (「空の」オブジェクトへの単一のオブジェクト参照のみであるため、状態がない場合はほぼ 0 です)。

于 2009-07-21T21:23:50.927 に答える
0

別の可能なアプローチ:

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
}

少なくとも場合によってはそれで行くかもしれません。

于 2009-07-31T12:56:46.797 に答える