Frage

Daher habe ich auf SO und anderswo etwa 20 Beispiele dazu durchgesehen, aber keines gefunden, das das abdeckt, was ich versuche.Das - Kann ich meinen expliziten Typkomparator inline angeben? - sieht aus wie das, was ich brauche, geht aber nicht weit genug (oder ich verstehe nicht, wie ich es weiterführen soll).

  • Ich habe eine Liste von LoadData. Das LoadData-Objekt enthält Felder sowohl vom Referenz- als auch vom Werttyp
  • Sie müssen eine Mischung aus Referenz- und Wertfeldern gruppieren und die Ausgabe auf einen anonymen Typ projizieren
  • Ich muss (glaube) einen benutzerdefinierten IEqualityComparer bereitstellen, um anzugeben, wie die GroupBy-Felder verglichen werden sollen, aber es handelt sich um einen anonymen Typ

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

Die beste GroupBy-Abfrage, die ich bisher gemacht habe:

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

Dies sind Gruppen, aber es gibt immer noch Duplikate.

  1. Wie kann ich benutzerdefinierten Code zum Vergleichen der GroupBy-Felder angeben?Beispielsweise könnten die Komponenten durch Component.Code verglichen werden.
War es hilfreich?

Lösung

Das Problem hierbei ist, dass Ihr Schlüsseltyp anonym ist, was bedeutet, dass Sie keine Klasse deklarieren können, die implementiert IEqualityComparer<T> für diesen Schlüsseltyp.Während es so wäre möglich Einen Komparator zu schreiben, der anonyme Typen auf benutzerdefinierte Weise (über eine generische Methode, Delegaten und Typinferenz) auf Gleichheit vergleicht, wäre nicht besonders angenehm.

Die zwei einfachsten Optionen sind wahrscheinlich:

  • Sorgen Sie dafür, dass der anonyme Typ „einfach funktioniert“, indem Sie Equals/GetHashCode in überschreiben PeriodEndDto Und ComponentDto.Wenn es eine natürliche Gleichheit gibt, die Sie überall verwenden möchten, ist dies wahrscheinlich die vernünftigste Option.Ich würde die Implementierung empfehlen IEquatable<T> sowie
  • Verwenden Sie zum Gruppieren keinen anonymen Typ. Verwenden Sie einen benannten Typ, und Sie können dann entweder einen anderen Typ überschreiben GetHashCode Und Equals darauf, oder Sie könnten auf normale Weise einen benutzerdefinierten Gleichheitsvergleicher schreiben.

BEARBEITEN: ProjectionEqualityComparer würde nicht wirklich funktionieren.Es wäre jedoch möglich, etwas Ähnliches zu schreiben – sozusagen CompositeEqualityComparer Dadurch konnten Sie einen Gleichheitsvergleicher aus mehreren „Projektion + Vergleicher“-Paaren erstellen.Im Vergleich zum anonymen Typ wäre es jedoch ziemlich hässlich.

Andere Tipps

BEARBEITEN:

Wie Jon Skeet betont, scheint diese Lösung besser zu sein, als sie ist, wenn man nicht zu lange darüber nachdenkt, weil ich vergessen habe, GetHashCode zu implementieren.Gethashcode implementieren zu müssen, macht diesen Ansatz, wie Jon in seiner Antwort sagt: "Nicht besonders angenehm". Vermutlich ist dies auch die Erklärung für das (sogenannte "unerklärliche) Abwesenheit von EqualityComparer<T>.Create() im Rahmen.Ich überlasse die Antwort als Referenz, da Beispiele dafür, was man nicht tun sollte, ebenfalls aufschlussreich sein können.

URSPRÜNGLICHE ANTWORT:

Sie könnten den von der vorgeschlagenen Ansatz verwenden Comparer<T>.Create Muster, das in .NET 4.5 eingeführt wurde (aber unerklärlicherweise fehlt in EqualityComparer<T>).Erstellen Sie dazu eine DelegateEqualityComparer<T> Klasse:

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

Schreiben Sie dann Wrapper um die GroupBy-Methoden, um a zu akzeptieren Func<TKey, TKey, bool> Delegierter anstelle des IEqualityComparer<TKey> Parameter.Diese Methoden umschließen den Delegaten in einem DelegateEqualityComparer<T> Instanz und geben Sie diese an die entsprechende GroupBy-Methode weiter.Beispiel:

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

Schließlich würden Sie an Ihrer Anrufstelle so etwas wie diesen Ausdruck für verwenden equalityComparison Streit:

(a, b) => a.PeriodEnd.Equals(b.PeriodEnd)
    && a.Component.Code.Equals(b.Component.Code)
    && a.GroupCode.Equals(b.GroupCode)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top