Widziałem, jak niektórzy programiści używają tego:
foreach (var item in items)
{
if (item.Field != null)
continue;
if (item.State != ItemStates.Deleted)
continue;
// code
}
zamiast tego, gdzie normalnie użyłbym:
foreach (var item in items.Where(i => i.Field != null && i.State != ItemStates.Deleted))
{
// code
}
Widziałem nawet kombinację obu. Bardzo podoba mi się czytelność z „kontynuuj”, szczególnie w bardziej złożonych warunkach. Czy jest jakaś różnica w wydajności? Zakładam, że z zapytaniem do bazy danych byłoby. Co ze zwykłymi listami?
c#
readability
loops
filtering
Papryka
źródło
źródło
Odpowiedzi:
Uznałbym to za odpowiednie miejsce do stosowania rozdzielania poleceń / zapytań . Na przykład:
Pozwala to również na nadanie dobrej, samo dokumentującej nazwy wynikowi zapytania. Pomaga także dostrzec możliwości refaktoryzacji, ponieważ o wiele łatwiej jest refaktoryzować kod, który tylko wysyła zapytania do danych lub tylko mutuje dane, niż kod mieszany, który próbuje wykonać jedno i drugie.
Podczas debugowania możesz przerwać wcześniej,
foreach
aby szybko sprawdzić, czy zawartość jestvalidItems
zgodna z oczekiwaniami. Nie musisz wchodzić w lambda, chyba że musisz. Jeśli musisz wejść w lambda, sugeruję, aby wydzielić ją na osobną funkcję, a następnie przejdź przez to.Czy jest jakaś różnica w wydajności? Jeśli zapytanie jest wspierane przez bazę danych, wersja LINQ może potencjalnie działać szybciej, ponieważ zapytanie SQL może być bardziej wydajne. Jeśli jest to LINQ względem Objects, nie zobaczysz żadnej prawdziwej różnicy w wydajności. Jak zawsze, profiluj swój kod i napraw zgłaszane wąskie gardła, zamiast próbować z góry przewidywać optymalizacje.
źródło
IEnumerable
jest tylko przezforeach
pętlę.Where
lambda i ciało pętli (jeśli lambda zwróci wartość true) raz na element.Oczywiście istnieje różnica w wydajności,
.Where()
w wyniku czego dla każdego pojedynczego elementu wykonywane jest połączenie delegowane. Jednak nie martwiłbym się w ogóle wydajnością:Cykle zegarowe używane podczas wywoływania delegata są pomijalne w porównaniu z cyklami zegarowymi używanymi przez resztę kodu, który iteruje kolekcję i sprawdza warunki.
Kara za wydajność wywołania delegata jest rzędu kilku cykli zegara i na szczęście już dawno minęły czasy, kiedy musieliśmy się martwić o poszczególne cykle zegara.
Jeśli z jakiegoś powodu wydajność jest dla Ciebie bardzo ważna na poziomie cyklu zegara, użyj
List<Item>
zamiastIList<Item>
, aby kompilator mógł korzystać z bezpośrednich (i nieuchronnych) wywołań zamiast wywołań wirtualnych i aby iteratorList<T>
, który jest w rzeczywistości astruct
, nie musi być zapakowane. Ale to naprawdę drobiazgi.Zapytanie do bazy danych jest inną sytuacją, ponieważ istnieje (przynajmniej teoretycznie) możliwość wysłania filtra do RDBMS, co znacznie poprawia wydajność: tylko dopasowanie wierszy spowoduje przejście z RDBMS do twojego programu. Ale do tego myślę, że musiałbyś użyć linq, nie sądzę, aby to wyrażenie mogło zostać wysłane do RDBMS w obecnej formie.
Naprawdę zobaczysz korzyści z
if(x) continue;
momentu, w którym będziesz musiał debugować ten kod: Pojedyncze przeskakiwanie międzyif()
s icontinue
s ładnie działa; pojedynczy krok do filtrowania delegata jest uciążliwy.źródło
if(x) continue;
..Where
wywoływana jest tylko raz. Co zostanie wywołana w każdej iteracji jest delegatem filtr (aMoveNext
iCurrent
na wyliczający, gdy nie otrzymasz zoptymalizowaną out).Where
tylko raz. Naprawione.