Ich habe gesehen, dass einige Programmierer dies verwenden:
foreach (var item in items)
{
if (item.Field != null)
continue;
if (item.State != ItemStates.Deleted)
continue;
// code
}
anstatt wo ich normalerweise verwenden würde:
foreach (var item in items.Where(i => i.Field != null && i.State != ItemStates.Deleted))
{
// code
}
Ich habe sogar eine Kombination von beiden gesehen. Ich mag die Lesbarkeit mit 'Fortfahren' sehr, besonders bei komplexeren Bedingungen. Gibt es überhaupt einen Leistungsunterschied? Bei einer Datenbankabfrage gehe ich davon aus, dass dies der Fall sein würde. Was ist mit regulären Listen?
c#
readability
loops
filtering
Paprik
quelle
quelle
Antworten:
Ich würde dies als einen geeigneten Ort betrachten, um die Trennung von Befehlen und Abfragen zu verwenden . Beispielsweise:
Auf diese Weise können Sie dem Abfrageergebnis auch einen guten, selbstdokumentierenden Namen geben. Es hilft Ihnen auch dabei, Möglichkeiten für die Umgestaltung zu erkennen, da es viel einfacher ist, Code umzugestalten, der nur Daten abfragt oder nur Daten mutiert, als gemischten Code, der versucht, beides zu tun.
Beim Debuggen können Sie eine Pause einlegen,
foreach
um schnell zu überprüfen, ob der Inhalt dervalidItems
Lösung Ihren Erwartungen entspricht. Sie müssen nicht ins Lambda steigen, es sei denn, Sie müssen. Wenn Sie in das Lambda einsteigen müssen, dann schlage ich vor, es in eine separate Funktion zu zerlegen und diese stattdessen durchzugehen.Gibt es einen Leistungsunterschied? Wenn die Abfrage von einer Datenbank gesichert wird, kann die LINQ-Version möglicherweise schneller ausgeführt werden, da die SQL-Abfrage möglicherweise effizienter ist. Wenn es sich um LINQ to Objects handelt, werden Sie keinen echten Leistungsunterschied feststellen. Profilieren Sie Ihren Code wie immer und beheben Sie die tatsächlich gemeldeten Engpässe, anstatt im Voraus Optimierungen vorherzusagen.
quelle
IEnumerable
wird nur von derforeach
Schleife gesteuert .Where
Lambda und den Schleifenkörper (wenn das Lambda true zurückgibt) einmal pro Element aus.Natürlich gibt es einen Unterschied in der Leistung, was dazu
.Where()
führt, dass für jedes einzelne Element ein Delegiertenanruf durchgeführt wird. Um die Performance würde ich mir jedoch überhaupt keine Sorgen machen:Die beim Aufrufen eines Delegaten verwendeten Taktzyklen sind im Vergleich zu den Taktzyklen, die vom Rest des Codes verwendet werden, der die Auflistung durchläuft und die Bedingungen überprüft, vernachlässigbar.
Der Leistungsnachteil beim Aufrufen eines Delegaten liegt in der Größenordnung einiger Taktzyklen, und glücklicherweise sind wir längst über die Tage hinausgegangen, an denen wir uns um einzelne Taktzyklen kümmern mussten.
Wenn aus irgendeinem Grund die Leistung auf Taktebene für Sie wirklich wichtig ist, verwenden Sie
List<Item>
stattdessenIList<Item>
, damit der Compiler direkte (und inlinierbare) Aufrufe anstelle von virtuellen Aufrufen verwenden kann, und damit der Iterator vonList<T>
, der tatsächlich ist astruct
, muss nicht eingepackt werden. Aber das ist wirklich Kleinigkeit.Eine Datenbankabfrage stellt eine andere Situation dar, da (zumindest theoretisch) die Möglichkeit besteht, den Filter an das RDBMS zu senden, was die Leistung erheblich verbessert: Nur übereinstimmende Zeilen lösen die Reise vom RDBMS zu Ihrem Programm aus. Aber dafür müssten Sie wahrscheinlich linq verwenden. Ich glaube nicht, dass dieser Ausdruck so wie er ist an das RDBMS gesendet werden kann.
Sie werden die Vorteile sofort erkennen,
if(x) continue;
wenn Sie diesen Code debuggen müssen: Das einfache Überschreiten vonif()
s undcontinue
s funktioniert einwandfrei. Ein einziger Schritt in den Filter-Delegierten ist ein Schmerz.quelle
if(x) continue;
..Where
nur einmal aufgerufen. Was bei jeder Iteration aufgerufen wird , ist der Filter delegiert (undMoveNext
undCurrent
auf dem Enumerator, wenn sie nicht bekommen , optimiert out).Where
nur einmal aufgerufen. Behoben.