Domanda

Non ho mai scritto una IComparer<T> stateful con un costruttore di default. Tutte le biblioteche implementazioni standard che ho controllato in Reflector sono apolidi pure. Pertanto, mi piacerebbe pensare che posso liberamente in cache IComparer<T> in questo modo:

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

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

anziché

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

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

    ...
}

Quindi, ecco la domanda: avete mai scritto / visto un IComparer<T> per il quale questo sarebbe abbattere? Se sì, come comune è?

EDIT: Il motivo per cui non voglio il sovraccarico della seconda versione in questo caso è che la struttura dei dati è persistente. Si è implementato come un albero in cui i nodi hanno alcun riferimento genitore / root. Quindi non sarebbe stato un riferimento a di confronto per coda, ma un riferimento a di confronto per nodo! Il mio progetto originale era solo per usare IComparable<T> e raccomandare a scrivere una struct wrapper per i confronti personalizzati.

È stato utile?

Soluzione

Bene, avere un static di confronto significa che non si possono avere i confronti differenti su diverse code; questo può essere un problema ... di tanto in tanto la gente do bisogno di un confronto personalizzato; per esempio, se non controllano il tipo. Il mio approccio di default potrebbe essere:

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

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

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

Re comparatori stateful; Sì, ho scritto diversi - in particolare per la scrittura di comparatori di proiezione in stile LINQ ... per esempio, qualcosa come:

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

che consente di:

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

istanza "Ordina per nome" confronto.

Altri suggerimenti

Sì, ho, ma penso che sia abbastanza raro.

Ci sono rari casi in cui si desidera implementare un confronto che è dipendente da altri dati. Per esempio, abbiamo alcune routine spaziali in cui nutriamo un asse che viene utilizzato per il confronto come parte della IComparer.

Detto questo, è abbastanza facile per risolvere questo da solo utilizzando una classe di confronto separato, e questo è probabilmente una migliore progettazione in molti modi. Si sta mettendo una limitazione sull'attuazione IComparer<T> che non ha bisogno di esistere, però, quindi vorrei documentare la vostra logica.

La mia preferenza personale sarebbe quello di rendere il IComparer<T> non statico, e di fornire due costruttori - uno che prende un'istanza esterna e uno che crea un operatore di confronto di default. Hai il sovraccarico di un operatore di confronto per coda, ma questo è piuttosto minimale (quasi 0 se si dispone di nessuno stato, dal momento che è un solo riferimento a un oggetto a un oggetto "vuoto").

Un altro approccio possibile:

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
}

io possa andare con lui, almeno per alcuni casi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top