93 Stimmen

Sollte ich zwei "where"-Klauseln oder "&&" in meiner LINQ-Abfrage verwenden?

Wenn ich eine LINQ-Abfrage mit mehreren "und"-Bedingungen schreibe, sollte ich eine einzelne where Klausel mit && oder mehrere where Klauseln, eine für jede Bedingung?

static void Main(string[] args)
{
    var ints = new List<int>(Enumerable.Range(-10, 20));

    var positiveEvensA = from i in ints
                         where (i > 0) && ((i % 2) == 0)
                         select i;

    var positiveEvensB = from i in ints
                         where i > 0
                         where (i % 2) == 0
                         select i;

    System.Diagnostics.Debug.Assert(positiveEvensA.Count() == 
                                         positiveEvensB.Count());
}

Gibt es außer der persönlichen Vorliebe oder dem Kodierungsstil (lange Zeilen, Lesbarkeit usw.) noch andere Unterschiede zwischen positivEvensA y positiveEvensB ?

Ein möglicher Unterschied besteht darin, dass verschiedene LINQ-Provider möglicherweise besser in der Lage sind, mit mehreren where s statt eines komplexeren Ausdrucks. Stimmt das?

61voto

Reed Copsey Punkte 536986

Ich persönlich würde mich immer für && und nicht für zwei where-Klauseln entscheiden, wenn die Aussage dadurch nicht unverständlich wird.

In Ihrem Fall wird es wahrscheinlich überhaupt nicht auffallen, aber 2 where-Klauseln haben definitiv Auswirkungen auf die Leistung, wenn Sie eine große Auflistung haben und wenn Sie alle Ergebnisse dieser Abfrage verwenden. Wenn Sie beispielsweise .Count() für die Ergebnisse aufrufen oder die gesamte Liste durchlaufen, wird die erste Where-Klausel ausgeführt und eine neue IEnumerable<T> erstellt, die erneut vollständig aufgezählt wird, und zwar mit einem zweiten Delegaten.

Die Verkettung der beiden Klauseln führt dazu, dass die Abfrage einen einzigen Delegaten bildet, der ausgeführt wird, wenn die Sammlung aufgezählt wird. Dies führt zu einer Aufzählung durch die Sammlung und einem Aufruf des Delegaten jedes Mal, wenn ein Ergebnis zurückgegeben wird.

Wenn man sie aufteilt, ändern sich die Dinge. Während die erste Where-Klausel die ursprüngliche Auflistung aufzählt, zählt die zweite Where-Klausel deren Ergebnisse auf. Dies verursacht potenziell (im schlimmsten Fall) 2 vollständige Aufzählungen durch Ihre Sammlung und 2 Delegierte, die pro Mitglied aufgerufen werden, was bedeuten könnte, dass diese Anweisung (theoretisch) die doppelte Laufzeitgeschwindigkeit benötigen könnte.

Wenn Sie sich für die Verwendung von 2 Where-Klauseln entscheiden, ist es hilfreich, die restriktivere Klausel zuerst zu platzieren, da die zweite Where-Klausel nur auf die Elemente angewendet wird, die die erste Klausel bestehen.

In Ihrem Fall spielt das keine Rolle. Bei einer großen Sammlung könnte es aber eine Rolle spielen. Als allgemeine Faustregel gilt für mich:

  1. Lesbarkeit und Wartbarkeit

  2. Leistung

In diesem Fall denke ich, dass beide Optionen gleich gut zu pflegen sind, daher würde ich mich für die leistungsfähigere Option entscheiden.

27voto

JaredPar Punkte 699699

Dies ist vor allem eine Frage des persönlichen Stils. Ich persönlich finde es gut, wenn die where Klausel in eine Zeile passt, gruppiere ich die Klauseln.

Verwendung mehrerer where s sind in der Regel weniger performant, da sie für jedes Element, das es so weit schafft, einen zusätzlichen Delegatenaufruf erfordern. Allerdings ist es wahrscheinlich ein unbedeutendes Problem und sollte nur in Betracht gezogen werden, wenn ein Profiler zeigt es ein Problem zu sein.

17voto

Tim Schmelter Punkte 427304

Wie Jared Par bereits gesagt hat: Es hängt von Ihren persönlichen Vorlieben, der Lesbarkeit und dem Anwendungsfall ab. Wenn Ihre Methode zum Beispiel einige optionale Parameter hat und Sie eine Sammlung filtern wollen, wenn diese gegeben sind, kann die Where ist perfekt:

IEnumerable<SomeClass> matchingItems = allItems;
if(!string.IsNullOrWhiteSpace(name))
    matchingItems = matchingItems
       .Where(c => c.Name == name);
if(date.HasValue)
    matchingItems = matchingItems
       .Where(c => c.Date == date.Value);
if(typeId.HasValue)
    matchingItems = matchingItems
       .Where(c => c.TypeId == typeId.Value);
return matchingItems;

Wenn Sie dies tun wollten mit && viel Spaß ;)

Wo ich nicht zustimme Jared y Schilfrohr ist das Leistungsproblem, das mehrere Where haben sollen. Eigentlich Where ist so optimiert, dass es mehrere Prädikate zu einem kombiniert, wie Sie sehen können aquí (in CombinePredicates ).

Aber ich wollte wissen, ob es wirklich keine großen Auswirkungen hat, wenn die Sammlung groß ist und es mehrere Where die alle bewertet werden müssen. Ich war erstaunt, dass die folgenden Benchmark ergab, dass sogar die mehrfachen Where Ansatz war etwas effizienter. Die Zusammenfassung:

Methode

Mittlere

Fehler

StdDev

MehrereWo

1.555 s

0.0310 s

0.0392 s

MultipleAnd

1.571 s

0.0308 s

0.0649 s

Hier ist der Benchmark-Code, ich denke, er ist gut genug für diesen Test:

#LINQPad optimize+

void Main()
{
    var summary = BenchmarkRunner.Run<WhereBenchmark>();
}

public class WhereBenchmark
{
    string[] fruits = new string[] { "apple", "mango", "papaya", "banana", "guava", "pineapple" };
    private IList<string> longFruitList;

    [GlobalSetup]
    public void Setup()
    {
        Random rnd = new Random();
        int size = 1_000_000;
        longFruitList = new List<string>(size);
        for (int i = 1; i < size; i++)
            longFruitList.Add(GetRandomFruit());

        string GetRandomFruit()
        {
            return fruits[rnd.Next(0, fruits.Length)];
        }
    }

    [Benchmark]
    public void MultipleWhere()
    {
        int count = longFruitList
            .Where(f => f.EndsWith("le"))
            .Where(f => f.Contains("app"))
            .Where(f => f.StartsWith("pine"))
            .Count(); // counting pineapples
    }

    [Benchmark]
    public void MultipleAnd()
    {
        int count = longFruitList
            .Where(f => f.EndsWith("le") && f.Contains("app") && f.StartsWith("pine"))
            .Count(); // counting pineapples
    }
}

12voto

John Wardale Punkte 111

Das Leistungsproblem gilt nur für speicherbasierte Sammlungen ... Linq to SQL erzeugt Ausdrucksbäume, die die Ausführung aufschieben. Mehr Details hier:

Mehrere WHERE-Klauseln mit LINQ-Erweiterungsmethoden

2voto

Commander Punkte 281

Wenn Sie SQL Profiler ausführen und die generierten Abfragen überprüfen, können Sie feststellen, dass es keinen Unterschied zwischen den beiden Abfragetypen in Bezug auf die Leistung gibt. Es kommt also ganz auf Ihren Geschmack beim Codestil an.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X