Verwendung des Lambda-Ausdrucks anstelle des IComparer-Arguments

76

Ist es mit C # möglich, einen Lambda-Ausdruck als IComparer-Argument in einem Methodenaufruf zu übergeben?

zB so etwas wie

var x = someIEnumerable.OrderBy(aClass e => e.someProperty, 
(aClass x, aClass y) => 
  x.someProperty > y.SomeProperty ?  1 : x.someProperty < y.SomeProperty ?  -1 : 0);

Ich kann das nicht ganz kompilieren, also schätze ich nicht, aber es scheint eine so offensichtliche Synergie zwischen Lambdas und anonymen Delegierten zu sein, dass ich das Gefühl habe, etwas Dummes falsch machen zu müssen.

TIA

hochmütig
quelle
1
Mögliche Antwort hier: stackoverflow.com/questions/9824435/…
Chris Mantle

Antworten:

64

Wie Jeppe betont, können Sie unter .NET 4.5 die statische Methode verwenden Comparer<T>.Create.

Wenn nicht, ist dies eine Implementierung, die gleichwertig sein sollte:

public class FunctionalComparer<T> : IComparer<T>
{
    private Func<T, T, int> comparer;
    public FunctionalComparer(Func<T, T, int> comparer)
    {
        this.comparer = comparer;
    }
    public static IComparer<T> Create(Func<T, T, int> comparer)
    {
        return new FunctionalComparer<T>(comparer);
    }
    public int Compare(T x, T y)
    {
        return comparer(x, y);
    }
}
Timothy Shields
quelle
1
Möglicherweise möchten Sie dieser Klasse einen anderen Namen geben, um Konflikte mit der Klasse der Bibliothek zu vermeiden.
Servy
Syntaktisches Detail: Der Konstruktor einer generischen Klasse darf den <T>Teil des Klassennamens nicht enthalten .
Jeppe Stig Nielsen
91

Wenn Sie mit .NET 4.5 arbeiten, können Sie die statische Methode verwenden Comparer<aClass>.Create.

Dokumentation: Comparer<T>.CreateMethode .

Beispiel:

var x = someIEnumerable.OrderBy(e => e.someProperty, 
    Comparer<aClass>.Create((x, y) => x.someProperty > y.SomeProperty ?  1 : x.someProperty < y.SomeProperty ?  -1 : 0)
    );
Jeppe Stig Nielsen
quelle
1
Leider schmachten wir im .Net 3.5 Land! Kann nicht den Mega-Keil TFS auf die neueste Version :-( Upgrade benötigt leisten
haughtonomous
3
@haughtonomous Wenn das das einzige ist, was Sie zurückhält, haben Sie darüber nachgedacht, TFS zugunsten von etwas anderem zu entleeren?
Arturo Hernandez
Kennen Sie die wesentliche Theorie (nicht wie "da ein anderer Typ als ein Lambda erforderlich ist") darüber, warum wir Lambda nicht direkt dort platzieren können, sondern einen Wrapper benötigen?
jw_
@jw_ Ich bin mir nicht sicher, wie viel Theorie dahinter steckt. Die Autoren von .OrderBy(Linq) haben beschlossen, keine Überlastung zu haben, die einen Delegierten für den Vergleich akzeptiert (wie einen Comparison<TKey>Delegierten). Sie können Ihre eigene Erweiterungsmethode erstellen, wenn Sie möchten.
Jeppe Stig Nielsen
Die Theorie scheint zu sein, dass die Schnittstelle 2+ Methoden hat.
jw_
3

Wenn Sie projizierte Schlüssel konsistent vergleichen möchten (z. B. eine einzelne Eigenschaft), können Sie eine Klasse definieren, die die gesamte Schlüsselvergleichslogik für Sie kapselt, einschließlich Nullprüfungen, Schlüsselextraktion für beide Objekte und Schlüsselvergleich unter Verwendung des angegebenen oder standardmäßigen inneren Vergleicher:

public class KeyComparer<TSource, TKey> : Comparer<TSource>
{
    private readonly Func<TSource, TKey> _keySelector;
    private readonly IComparer<TKey> _innerComparer;

    public KeyComparer(
        Func<TSource, TKey> keySelector, 
        IComparer<TKey> innerComparer = null)
    {
        _keySelector = keySelector;
        _innerComparer = innerComparer ?? Comparer<TKey>.Default;
    }

    public override int Compare(TSource x, TSource y)
    {
        if (object.ReferenceEquals(x, y))
            return 0;
        if (x == null)
            return -1;
        if (y == null)
            return 1;

        TKey xKey = _keySelector(x);
        TKey yKey = _keySelector(y);
        return _innerComparer.Compare(xKey, yKey);
    }
}

Zur Vereinfachung eine Fabrikmethode:

public static class KeyComparer
{
    public static KeyComparer<TSource, TKey> Create<TSource, TKey>(
        Func<TSource, TKey> keySelector, 
        IComparer<TKey> innerComparer = null)
    {
        return new KeyComparer<TSource, TKey>(keySelector, innerComparer);
    }
}

Sie könnten dies dann folgendermaßen verwenden:

var sortedSet = new SortedSet<MyClass>(KeyComparer.Create((MyClass o) => o.MyProperty));

In meinem Blogbeitrag finden Sie eine ausführliche Beschreibung dieser Implementierung.

Douglas
quelle