Domanda

Così ho guardato attraverso circa 20 esempi in questo MODO e altrove, ma non ho trovato uno che copre quello che sto cercando di fare.Questo - Posso specificare il mio esplicito tipo di comparatore inline? - sembra che ciò che ho bisogno, ma non va abbastanza lontano (o io non riesco a capire come fare di più).

  • Ho un Elenco di LoadData, il LoadData oggetto ha dei campi di riferimento e i tipi di valore
  • Necessità di gruppo su una miscela di ref e campi di valore di progetto, l'uscita di un tipo anonimo
  • Bisogno (credo) per fornire un custom IEqualityComparer per specificare come confrontare il GroupBy campi, ma sono un tipo anonimo

    private class LoadData
    {
        public PeriodEndDto PeriodEnd { get; set; }
        public ComponentDto Component { get; set; }
        public string GroupCode { get; set; }
        public string PortfolioCode { get; set; }
    }
    

I migliori GroupBy query che ho di andare così lontano:

var distinctLoads = list.GroupBy(
    dl => new { PeriodEnd = dl.PeriodEnd, 
                Component = dl.Component, 
                GroupCode = dl.GroupCode },
    (key, data) => new {PeriodEnd = key.PeriodEnd, 
                Component = key.Component, 
                GroupCode = key.GroupCode, 
                PortfolioList = data.Select(d=>d.PortfolioCode)
                                    .Aggregate((g1, g2) => g1 + "," + g2)},
    null);

Questo gruppo, ma non ci sono ancora duplicati.

  1. Come posso specificare un codice personalizzato per confrontare GroupBy campi?Per esempio, i Componenti potrebbero essere confrontate Componente.Codice.
È stato utile?

Soluzione

Il problema qui è che il vostro tipo di chiave è anonimo, il che significa che non è possibile dichiarare una classe che implementa IEqualityComparer<T> per che tipo di chiave.Mentre sarebbe possibile per scrivere un comparatore che confronta tipi anonimi per la parità in modo personalizzato (tramite un metodo generico, delegati e il tipo di inferenza), non sarebbe terribilmente piacevole.

Le due opzioni più semplici sono probabilmente:

  • Fare il tipo anonimo "sufficiente" entro l'override del metodo Equals/GetHashCode in PeriodEndDto e ComponentDto.Se c'è una parità di diritti che si vuole utilizzare ovunque, questo è probabilmente l'opzione più sana.Mi consiglia di attuazione IEquatable<T> così
  • Non utilizzare un tipo anonimo per il raggruppamento di usare un nome di un tipo, e quindi è possibile eseguire l'override GetHashCode e Equals su questo, o si potrebbe scrivere un custom operatore di uguaglianza in modo normale.

EDIT: ProjectionEqualityComparer non funziona davvero.Sarebbe fattibile per scrivere qualcosa di simile, però, in una sorta di CompositeEqualityComparer che permette di creare un operatore di uguaglianza di diversi "proiezione + comparatore di" coppie.Sarebbe abbastanza brutto rispetto al tipo anonimo, però.

Altri suggerimenti

EDIT:

Come Jon Skeet, la soluzione sembra migliore di quello che è, se non pensare troppo su di esso, perché ho dimenticato di implementare GetHashCode.La necessità di implementare GetHashCode rende questo approccio, come Jon dice nella sua risposta, "non è terribilmente piacevole." Presumibilmente, questa è anche la spiegazione per il (cosiddetto "inspiegabile") assenza di EqualityComparer<T>.Create() nel quadro.Lascio la risposta a riferimento, come esempio di cosa non fare, può essere utile pure.

ORIGINALE RISPOSTA:

Si potrebbe utilizzare l'approccio suggerito dal Comparer<T>.Create modello introdotto nel .NET 4.5 (ma inspiegabilmente assenti in EqualityComparer<T>).Per farlo, creare un DelegateEqualityComparer<T> classe:

class DelegateEqualityComparer<T> : EqualityComparer<T>
{
    private readonly Func<T, T, bool> _equalityComparison;

    private DelegateEqualityComparer(Func<T, T, bool> equalityComparison)
    {
        if (equalityComparison == null)
            throw new ArgumentNullException("equalityComparison");
        _equalityComparison = equalityComparison;
    }

    public override bool Equals(T x, T y)
    {
        return _equalityComparison(x, y);
    }

    public static DelegateEqualityComparer<T> Create(
        Func<T, T, bool> equalityComparison)
    {
        return new DelegateEqualityComparer<T>(equalityComparison);
    }
}

Quindi scrivere wrapper intorno GroupBy metodi per accettare un Func<TKey, TKey, bool> delegato al posto del IEqualityComparer<TKey> parametro.Questi metodi a capo il delegato in un DelegateEqualityComparer<T> istanza, e che passare al corrispondente GroupBy metodo.Esempio:

public static class EnumerableExt
{
    public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(
        this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector,
        Func<TKey, TKey, bool> equalityComparison)
    {
        return source.GroupBy(
            keySelector,
            DelegateEqualityComparer<TKey>.Create(equalityComparison);
    }
}

Infine, presso il sito di chiamata, utilizzare qualcosa di simile a questa espressione per l' equalityComparison argomento:

(a, b) => a.PeriodEnd.Equals(b.PeriodEnd)
    && a.Component.Code.Equals(b.Component.Code)
    && a.GroupCode.Equals(b.GroupCode)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top