Chciałbym zrobić równoważne z następującymi w LINQ, ale nie wiem, jak to zrobić:
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
Jaka jest prawdziwa składnia?
linq
foreach
ienumerable
tags2k
źródło
źródło
ForEach()
.ForEach
rozszerzenie .foreach (var i in items) i.Dostuff();
Odpowiedzi:
Nie ma rozszerzenia ForEach dla
IEnumerable
; tylko dlaList<T>
. Więc możesz to zrobićMożesz też napisać własną metodę rozszerzenia ForEach:
źródło
IENumerable<T>
metodę rozszerzenia. Jak:public static IEnumerable<T> ForAll<T>(this IEnumerable<T> numeration, Action<T> action) { foreach (T item in enumeration) { action(item); } return enumeration; }
Fredrik dostarczył poprawkę, ale warto zastanowić się, dlaczego nie ma tego w ramce. Uważam, że pomysł polega na tym, aby operatory zapytań LINQ były wolne od efektów ubocznych, pasując do rozsądnie funkcjonalnego sposobu patrzenia na świat. Oczywiście ForEach jest dokładnie odwrotny - konstrukcja oparta wyłącznie na efektach ubocznych.
Nie oznacza to, że jest to zła rzecz - wystarczy pomyśleć o filozoficznych powodach tej decyzji.
źródło
Seq.iter
nic nie zwraca, nie mówiąc już o nowym seq z elementami ubocznymi. Tak naprawdę chodzi tylko o skutki uboczne. Być może myśliszSeq.map
, ponieważSeq.iter
jest dokładnie równoważny zForEach
metodą rozszerzenia naIEnumerabe<_>
.Aktualizacja 17.07.2012: Wygląda na to, że od C # 5.0 zachowanie
foreach
opisane poniżej zostało zmienione i „ użycieforeach
zmiennej iteracyjnej w zagnieżdżonym wyrażeniu lambda nie daje już nieoczekiwanych wyników ” . Ta odpowiedź nie dotyczy C # ≥ 5.0 .@John Skeet i wszyscy, którzy preferują słowo kluczowe foreach.
Problem z „foreach” w C # przed 5.0 polega na tym, że jest to niespójne z tym, jak działa odpowiednik „do zrozumienia” w innych językach, i z tym, jak bym się spodziewał, że zadziała (osobista opinia podana tutaj tylko dlatego, że inni wspominali o ich opinia na temat czytelności). Zobacz wszystkie pytania dotyczące „ Dostępu do zmodyfikowanego zamknięcia ”, a także „ Zamknięcia w pętli zmiennej uważanej za szkodliwą ”. Jest to „szkodliwe” tylko ze względu na sposób, w jaki „foreach” jest implementowany w języku C #.
Weź następujące przykłady, używając funkcjonalnie równoważnej metody rozszerzenia do tej z odpowiedzi @Fredrik Kalseth.
Przepraszamy za zbyt wymyślony przykład. Używam Observable, ponieważ zrobienie czegoś takiego nie jest zbyt dalekie. Oczywiście są lepsze sposoby na stworzenie tego, co można zaobserwować, staram się tylko wykazać, o co chodzi. Zazwyczaj kod subskrybowany do obserwowalnego jest wykonywany asynchronicznie i potencjalnie w innym wątku. Jeśli użyjesz „foreach”, może to dać bardzo dziwne i potencjalnie niedeterministyczne wyniki.
Następujący test przy użyciu metody rozszerzenia „ForEach” przechodzi pomyślnie:
Błąd kończy się następująco:
Oczekiwany: równoważny <0, 1, 2, 3, 4, 5, 6, 7, 8, 9> Ale był: <9, 9, 9, 9, 9, 9, 9, 9, 9, 9>
źródło
Możesz użyć
FirstOrDefault()
rozszerzenia, które jest dostępne dlaIEnumerable<T>
. Po powrociefalse
z predykatu zostanie on uruchomiony dla każdego elementu, ale nie będzie miało znaczenia, że tak naprawdę nie znajdzie dopasowania. Pozwoli to uniknąćToList()
kosztów ogólnych.źródło
foreach(Item i in GetItems()) { i.DoStuff();}
ciebie wziąłeś więcej postaci i sprawił, że było to bardzo myląceitems.All(i => { i.DoStuff(); return true; }
Wziąłem metodę Fredrika i zmodyfikowałem typ zwrotu.
W ten sposób metoda obsługuje odroczone wykonywanie podobnie jak inne metody LINQ.
EDYCJA: Jeśli nie było to jasne, każde użycie tej metody musi kończyć się na ToList () lub w jakikolwiek inny sposób, aby zmusić metodę do działania na kompletnym wyliczeniu. W przeciwnym razie akcja nie zostanie wykonana!
A oto test, który pomoże to zobaczyć:
Jeśli na końcu usuniesz ToList () , test zakończy się niepowodzeniem, ponieważ StringBuilder zawiera pusty ciąg. Jest tak, ponieważ żadna metoda nie wymusiła wyliczenia ForEach.
źródło
ForEach
jest interesująca, jednak nie pasuje do zachowaniaList.ForEach
, którego podpis jestpublic void ForEach(Action<T> action)
. Nie odpowiada również zachowaniuObservable.ForEach
rozszerzeniaIObservable<T>
, którego podpis jestpublic static void ForEach<TSource>(this IObservable<TSource> source, Action<TSource> onNext)
. FWIW,ForEach
odpowiednik kolekcji Scali, nawet te leniwe, mają również typ zwracany, który jest równoważny void w języku C #.Select
pomocąToList
. CelemForEach
jest, aby nie musieć dzwonićToList
. Powinien zostać wykonany natychmiast.Trzymaj swoje efekty uboczne poza moim IEnumerable
Jak zauważyli inni tutaj i za granicą, LINQ i
IEnumerable
metody powinny być wolne od skutków ubocznych.Czy naprawdę chcesz „zrobić coś” dla każdego elementu w IEnumerable? To
foreach
jest najlepszy wybór. Ludzie nie są zaskoczeni, gdy pojawiają się tutaj skutki uboczne.Założę się, że nie chcesz efektu ubocznego
Jednak z mojego doświadczenia wynika, że działania niepożądane zwykle nie są wymagane. Najczęściej jest czekające na odkrycie proste zapytanie LINQ wraz z odpowiedzią StackOverflow.com autorstwa Jona Skeeta, Erica Lipperta lub Marca Gravella wyjaśniającą, jak zrobić to, co chcesz!
Kilka przykładów
Jeśli faktycznie agregujesz (gromadzisz) jakąś wartość, powinieneś rozważyć
Aggregate
metodę rozszerzenia.Być może chcesz utworzyć nowy
IEnumerable
z istniejących wartości.A może chcesz utworzyć tabelę przeglądową:
Lista (kalambur nie do końca zamierzona) możliwości jest długa.
źródło
Jeśli chcesz działać jak rzuty wyliczeniowe, powinieneś dać każdy przedmiot.
źródło
Istnieje eksperymentalna wersja Microsoft Interaktywnych rozszerzeń do LINQ (także na NuGet , zobacz profil RxTeams, aby uzyskać więcej linków). Film na kanale 9 dobrze to wyjaśnia.
Dokumenty są dostarczane tylko w formacie XML. Uruchomiłem tę dokumentację w Sandcastle , aby była w bardziej czytelnym formacie. Rozpakuj archiwum dokumentów i poszukaj pliku index.html .
Wśród wielu innych korzyści zapewnia oczekiwaną implementację ForEach. Pozwala napisać taki kod:
źródło
Zgodnie z PLINQ (dostępne od .Net 4.0), możesz zrobić
zrobić równoległą pętlę foreach na IEnumerable.
źródło
Celem ForEach jest wywoływanie skutków ubocznych. IEnumerable służy do leniwego wyliczania zestawu.
Ta różnica pojęciowa jest dość widoczna, gdy się ją uwzględni.
SomeEnumerable.ForEach(item=>DataStore.Synchronize(item));
Nie będzie to wykonywane, dopóki nie wykonasz „count”, „ToList ()” lub czegoś w tym stylu. Wyraźnie nie jest to, co jest wyrażone.
Powinieneś używać IEnumerable rozszerzeń do konfigurowania łańcuchów iteracji, definiowania treści według ich odpowiednich źródeł i warunków. Drzewa ekspresji są potężne i wydajne, ale powinieneś nauczyć się doceniać ich naturę. I to nie tylko do programowania wokół nich, aby zapisać kilka znaków zastępujących leniwe oceny.
źródło
Wiele osób o tym wspominało, ale musiałem to zapisać. Czy to nie jest najbardziej jasne / najbardziej czytelne?
Krótkie i proste (st).
źródło
GetItems()
metoda zwraca.foreach (var item in GetItems()) item.DoStuff();
to tylko piękno.Teraz mamy opcję ...
Oczywiście otwiera to zupełnie nową puszkę owsików.
ps (Przepraszam za czcionki, tak zdecydował system)
źródło
Jak już wskazują liczne odpowiedzi, możesz z łatwością dodać taką metodę rozszerzenia samodzielnie. Jeśli jednak nie chcesz tego robić, chociaż nie znam czegoś takiego w BCL, nadal istnieje opcja w
System
przestrzeni nazw, jeśli masz już odniesienie do Reactive Extension (a jeśli nie masz , powinieneś mieć):Chociaż nazwy metod są nieco inne, końcowy wynik jest dokładnie tym, czego szukasz.
źródło
ForEach może być również Przykuty , wystarczy umieścić z powrotem do pileline po akcji. zachowaj płynność
Marzą wersja realizacji.
Edit: Lazy wersja używa zwrotu plastyczności, jak ten .
Wersja Lazy POTRZEBUJE się zmaterializować, na przykład ToList (), w przeciwnym razie nic się nie stanie. patrz poniżej świetne komentarze ToolmakerSteve.
W mojej bibliotece przechowuję zarówno ForEach (), jak i ForEachLazy ().
źródło
foreach(T item in enu) {action(item); yield return item;}
foreach (T item in enu) { action1(item); action2(item); action3(item); }
? Pojedyncza, wyraźna pętla. Wydaje mi się, że ważne było wykonanie wszystkich akcji PRZED rozpoczęciem wykonywania akcji 2.ProcessShipping()
? E-mail do kupującego, obciążenie jego karty kredytowej, ale nigdy nie wysyłasz mu swoich rzeczy? Z pewnością prosi o problemy.Ta abstrakcja „funkcjonalnego podejścia” przecieka przez długi czas. Nic na poziomie języka nie zapobiega skutkom ubocznym. Tak długo, jak możesz wywołać swoją lambda / delegata dla każdego elementu w kontenerze - otrzymasz zachowanie „ForEach”.
Oto na przykład jeden ze sposobów połączenia srcDictionary w destDictionary (jeśli klucz już istnieje - zastępuje)
jest to hack i nie należy go używać w żadnym kodzie produkcyjnym.
źródło
foreach(var tuple in srcDictionary) { destDictionary[tuple.Key] = tuple.Value; }
? Jeśli nie w kodzie produkcyjnym, to gdzie i dlaczego warto z tego korzystać?Zainspirowany przez Jona Skeeta, rozszerzyłem jego rozwiązanie o:
Metoda rozszerzenia:
Klient:
. . .
źródło
MoreLinq ma
IEnumerable<T>.ForEach
i mnóstwo innych przydatnych rozszerzeń. Prawdopodobnie nie warto brać pod uwagę zależnościForEach
, ale jest tam wiele przydatnych rzeczy.https://www.nuget.org/packages/morelinq/
https://github.com/morelinq/MoreLINQ
źródło
Nie zgadzam się z opinią, że metody rozszerzenia linków powinny być wolne od skutków ubocznych (nie tylko dlatego, że nie są, żaden delegat może wywoływać skutki uboczne).
Rozważ następujące:
To, co pokazuje przykład, to tak naprawdę rodzaj późnego wiązania, które pozwala wywołać jedną z wielu możliwych akcji mających skutki uboczne na sekwencji elementów, bez konieczności pisania dużego konstruktu przełączającego w celu zdekodowania wartości definiującej akcję i tłumaczenia do odpowiedniej metody.
źródło
Aby zachować biegłość, można zastosować taką sztuczkę:
źródło
W przypadku VB.NET należy użyć:
źródło
List
lubIList
. OgólnieIEnumerable
, napisz VB odpowiednika metody rozszerzenia ForEach przyjętej odpowiedzi .Jeszcze inny
ForEach
przykładźródło