Zainspirowany innym pytaniem dotyczącym brakującej Zip
funkcji:
Dlaczego ForEach
w Enumerable
klasie nie ma metody rozszerzenia ? Czy gdziekolwiek? Jedyną klasą, która otrzymuje ForEach
metodę, jest List<>
. Czy istnieje powód, dla którego go brakuje (wydajność)?
c#
.net
vb.net
extension-methods
Cameron MacFarland
źródło
źródło
Parallel.ForEach
na,Enumerable.ForEach
aby tylko odkryć ten drugi nie istnieje. C # przeoczył sztuczkę, aby to ułatwić.Odpowiedzi:
W języku, który wykonuje pracę przez większość czasu, jest już zawarte zdanie foreach.
Nie chciałbym widzieć następujących rzeczy:
Zamiast:
Ten ostatni jest bardziej przejrzysty i łatwiejszy do odczytania w większości sytuacji , choć może trochę dłużej pisać.
Muszę jednak przyznać, że zmieniłem stanowisko w tej sprawie; metoda rozszerzenia ForEach () rzeczywiście byłaby przydatna w niektórych sytuacjach.
Oto główne różnice między instrukcją a metodą:
To wszystko są świetne uwagi wielu ludzi tutaj i rozumiem, dlaczego ludzie tęsknią za tą funkcją. Nie miałbym nic przeciwko dodaniu przez Microsoft standardowej metody ForEach w następnej iteracji frameworka.
źródło
foreach
jest sprawdzane pod względem typu w czasie kompilacji, jeśli parametr wymienny jest sparametryzowany. Brakuje tylko sprawdzania typu czasu kompilacji dla kolekcji innych niż ogólne, podobnie jak hipotetycznaForEach
metoda rozszerzenia.col.Where(...).OrderBy(...).ForEach(...)
. Znacznie prostsza składnia, brak zanieczyszczenia ekranu dzięki redundantnym zmiennym lokalnym lub długim nawiasom kwadratowym w foreach ().list.ForEach(item => item.DoSomething())
. A kto mówi, że to lista? Oczywisty przypadek użycia znajduje się na końcu łańcucha; z instrukcją foreach musiałbyś położyć cały łańcuchin
, a operację wykonać w bloku, co jest znacznie mniej naturalne lub spójne.Metoda ForEach została dodana przed LINQ. Jeśli dodasz rozszerzenie ForEach, nigdy nie będzie ono wywoływane dla instancji List z powodu ograniczeń metod rozszerzenia. Myślę, że powodem, dla którego nie został dodany, jest brak ingerencji w istniejącą.
Jeśli jednak naprawdę tęsknisz za tą małą fajną funkcją, możesz wypuścić własną wersję
źródło
Możesz napisać tę metodę rozszerzenia:
Plusy
Pozwala na łączenie:
Cons
W rzeczywistości nic nie zrobi, dopóki nie zrobisz czegoś, aby wymusić iterację. Z tego powodu nie należy go nazywać
.ForEach()
. Możesz napisać.ToList()
na końcu lub możesz również napisać tę metodę rozszerzenia:Może to być zbyt znaczące odstępstwo od wysyłanych bibliotek C #; czytelnicy, którzy nie znają metod rozszerzenia, nie będą wiedzieli, co zrobić z twoim kodem.
źródło
{foreach (var e in source) {action(e);} return source;}
numbers.Select(n => n*2);
Select()
mówi , że stosuje funkcję do każdego elementu i zwraca wartość funkcji, w którejApply()
mówi, że stosuje sięAction
do każdego elementu i zwraca oryginalny element.Select(e => { action(e); return e; })
Dyskusja tutaj daje odpowiedź:
Zasadniczo podjęto decyzję o tym, aby metody rozszerzenia były funkcjonalnie „czyste”. ForEach zachęcałby do skutków ubocznych przy stosowaniu metod rozszerzania Enumerable, co nie było zamierzone.
źródło
Chociaż zgadzam się, że lepiej jest używać wbudowanej
Przykładforeach
konstrukcji w większości przypadków, uważam, że użycie tej odmiany rozszerzenia ForEach <> jest nieco przyjemniejsze niż samodzielne zarządzanie indeksemforeach
:Dałby ci:
źródło
Jednym obejściem jest napisanie
.ToList().ForEach(x => ...)
.profesjonaliści
Łatwy do zrozumienia - czytelnik musi tylko wiedzieć, co jest dostarczane z C #, a nie żadnych dodatkowych metod rozszerzenia.
Szum syntaktyczny jest bardzo łagodny (dodaje tylko trochę ekstrawaganckiego kodu).
Zwykle nie kosztuje to dodatkowej pamięci, ponieważ natywny i
.ForEach()
tak musiałby zrealizować całą kolekcję.Cons
Kolejność operacji nie jest idealna. Wolę zdać sobie sprawę z jednego elementu, a następnie zastosować go, a następnie powtórzyć. Ten kod najpierw realizuje wszystkie elementy, a następnie działa na nie kolejno.
Jeśli realizacja listy spowoduje wyjątek, nigdy nie będziesz działać na jednym elemencie.
Jeśli wyliczenie jest nieskończone (jak liczby naturalne), nie masz szczęścia.
źródło
Enumerable.ForEach<T>
metody, istniejeList<T>.ForEach
metoda. c) „Zwykle nie kosztuje dodatkowej pamięci, ponieważ natywna .ForEach () i tak musiałaby zrealizować całą kolekcję.” - kompletny i kompletny nonsens. Oto implementacja Enumerable.ForEach <T>, którą zapewniłby C #, gdyby to zrobił:public static void ForEach<T>(this IEnumerable<T> list, Action<T> action) { foreach (T item in list) action(item); }
Zawsze się nad tym zastanawiałem, dlatego zawsze noszę to ze sobą:
Fajna mała metoda rozszerzenia.
źródło
Pojawiło się wiele komentarzy na temat tego, że metoda rozszerzenia ForEach nie jest odpowiednia, ponieważ nie zwraca wartości takiej jak metody rozszerzenia LINQ. Chociaż jest to stwierdzenie oparte na faktach, nie jest ono do końca prawdą.
Wszystkie metody rozszerzenia LINQ zwracają wartość, dzięki czemu można je połączyć w łańcuch:
Jednak fakt, że LINQ jest implementowany przy użyciu metod rozszerzenia, nie oznacza, że metody rozszerzenia muszą być używane w ten sam sposób i zwracają wartość. Pisanie metody rozszerzenia w celu ujawnienia wspólnej funkcjonalności, która nie zwraca wartości, jest całkowicie poprawnym zastosowaniem.
Konkretny argument dotyczący ForEach polega na tym, że w oparciu o ograniczenia dotyczące metod rozszerzenia (a mianowicie, że metoda rozszerzenia nigdy nie zastąpi odziedziczonej metody o tej samej sygnaturze ), może wystąpić sytuacja, w której niestandardowa metoda rozszerzenia jest dostępna we wszystkich klasach, które stymulują IEnumerable
<T
> oprócz List<T
>. Może to powodować zamieszanie, gdy metody zaczną zachowywać się inaczej, w zależności od tego, czy wywoływana jest metoda rozszerzenia, czy metoda dziedziczenia.źródło
Możesz użyć (łańcuchowy, ale leniwie oceniany)
Select
, najpierw wykonując operację, a następnie zwracając tożsamość (lub coś innego, jeśli wolisz)Będziesz musiał upewnić się, że nadal jest oceniany, za pomocą
Count()
(najtańszej operacji do wyliczenia afaik) lub innej operacji, której i tak potrzebujesz.Chciałbym jednak, aby został on wprowadzony do standardowej biblioteki:
Powyższy kod staje się wtedy
people.WithLazySideEffect(p => Console.WriteLine(p))
faktycznie równoważny z foreach, ale leniwy i możliwy do połączenia.źródło
Select
już nie robi tego, co powinno. jest to zdecydowanie rozwiązanie dla tego, o co prosi OP - ale w kwestii czytelności tematu: nikt nie spodziewałby się, że select wykona funkcję.Select
nie zrobi tego, co powinien, to jest problem z implementacjąSelect
, a nie z wywoływanym kodemSelect
, który nie ma wpływu na to, coSelect
robi.Zauważ, że MoreLINQ NuGet zapewnia
ForEach
metodę rozszerzenia, której szukasz (a takżePipe
metodę, która wykonuje przekazanie i daje wynik). Widzieć:źródło
@Coincoin
Prawdziwa siła metody rozszerzenia foreach polega na możliwości ponownego użycia
Action<>
bez dodawania niepotrzebnych metod do kodu. Powiedz, że masz 10 list i chcesz wykonywać na nich tę samą logikę, a odpowiednia funkcja nie pasuje do twojej klasy i nie jest ponownie używana. Zamiast mieć dziesięć dla pętli lub funkcję ogólną, która jest oczywiście pomocnikiem, który nie należy, możesz zachować całą logikę w jednym miejscu (Action<>
. Tak więc, dziesiątki linii zastępuje sięitp...
Logika jest w jednym miejscu i nie zanieczyściłeś swojej klasy.
źródło
f(p)
, a następnie pozostawić na zewnątrz{ }
do skrótów:for (var p in List1.Concat(List2).Concat(List2)) f(p);
.Większość metod rozszerzenia LINQ zwraca wyniki. ForEach nie pasuje do tego wzoru, ponieważ nic nie zwraca.
źródło
public IEnumerable<T> Foreach<T>(this IEnumerable<T> items, Action<T> action) { /* error check */ foreach (var item in items) { action(item); yield return item; } }
Jeśli masz F # (który będzie w następnej wersji .NET), możesz użyć
Seq.iter doSomething myIEnumerable
źródło
Częściowo dzieje się tak, ponieważ projektanci języków nie zgadzają się z tym z filozoficznego punktu widzenia.
https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/
źródło
Możesz użyć opcji select, gdy chcesz coś zwrócić. Jeśli tego nie zrobisz, możesz najpierw użyć ToList, ponieważ prawdopodobnie nie chcesz niczego modyfikować w kolekcji.
źródło
Napisałem o tym post na blogu: http://blogs.msdn.com/kirillosenkov/archive/2009/01/31/foreach.aspx
Możesz głosować tutaj, jeśli chcesz zobaczyć tę metodę w .NET 4.0: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=279093
źródło
W wersji 3.5 wszystkie metody rozszerzeń dodane do IEnumerable są dostępne dla obsługi LINQ (zauważ, że są one zdefiniowane w klasie System.Linq.Enumerable). W tym poście wyjaśniam, dlaczego foreach nie należy do LINQ: istniejąca metoda rozszerzenia LINQ podobna do Parallel.For?
źródło
Czy to ja, czy lista <T>. Poszukiwania zostały prawie całkowicie przestarzałe przez Linq. Pierwotnie było
gdzie Y po prostu musiał być IEnumerable (Pre 2.0) i zaimplementować GetEnumerator (). Jeśli spojrzysz na wygenerowany MSIL, zobaczysz, że jest dokładnie taki sam jak
(Zobacz http://alski.net/post/0a-for-foreach-forFirst-forLast0a-0a-.aspx dla MSIL)
Następnie w DotNet2.0 pojawiła się Generics i Lista. Foreach zawsze uważał mnie za implementację wzorca Vistora (patrz Wzory projektowe Gamma, Helm, Johnson, Vlissides).
Teraz oczywiście w wersji 3.5 możemy zamiast tego użyć Lambdy do tego samego efektu, na przykład spróbuj http://dotnet-developments.blogs.techtarget.com/2008/09/02/iterators-lambda-and-linq-oh- mój/
źródło
Chciałbym rozwinąć odpowiedź Aku .
Jeśli chcesz wywołać metodę wyłącznie w celu uzyskania efektu ubocznego bez iteracji najpierw całej liczby, możesz użyć tego:
źródło
Select(x => { f(x); return x;})
Nikt jeszcze nie zauważył, że ForEach <T> powoduje sprawdzenie typu czasu kompilacji, w którym sprawdzane jest słowo kluczowe foreach.
Po dokonaniu refaktoryzacji w przypadku użycia obu metod w kodzie, faworyzuję .ForEach, ponieważ musiałem wyłapać błędy testowe / błędy w czasie wykonywania, aby znaleźć problemy z Foreach.
źródło
foreach
ma mniej sprawdzania czasu kompilacji. Jasne, jeśli twoja kolekcja nie jest ogólną IEnumerable, zmienna pętli będzie zmiennaobject
, ale to samo byłoby prawdą w przypadku hipotetycznejForEach
metody rozszerzenia.