Dlaczego w IEnumerable nie ma metody rozszerzenia ForEach?

389

Zainspirowany innym pytaniem dotyczącym brakującej Zipfunkcji:

Dlaczego ForEachw Enumerableklasie nie ma metody rozszerzenia ? Czy gdziekolwiek? Jedyną klasą, która otrzymuje ForEachmetodę, jest List<>. Czy istnieje powód, dla którego go brakuje (wydajność)?

Cameron MacFarland
źródło
Jest to prawdopodobnie wskazane przez wcześniejszy post, w tym foreach jest obsługiwany jako słowo kluczowe języka w C # i VB.NET (i wielu innych) Większość ludzi (w tym ja) po prostu pisze je same, jeśli jest to potrzebne i właściwe.
chrisb
2
Powiązane pytanie tutaj z linkiem do „oficjalnej odpowiedzi” stackoverflow.com/questions/858978/…
Benjol
1
Zobacz także stackoverflow.com/questions/200574/...
goodeye
Myślę, że jedną bardzo ważną kwestią jest to, że łatwiej jest dostrzec pętlę Foreach. Jeśli używasz .ForEach (...), łatwo przeoczyć ten, gdy przeglądasz kod. Staje się to ważne, gdy masz problemy z wydajnością. Oczywiście .ToList () i .ToArray () mają ten sam problem, ale są używane nieco inaczej. Innym powodem może być to, że bardziej powszechne jest w .ForEach modyfikowanie listy źródeł (np. Usuwanie / dodawanie elementów), aby nie było już „czystym” zapytaniem.
Daniel Bişar
Podczas debugowania sparaliżowanego kodu często próbuję zamienić Parallel.ForEachna, Enumerable.ForEachaby tylko odkryć ten drugi nie istnieje. C # przeoczył sztuczkę, aby to ułatwić.
Pułkownik Panic

Odpowiedzi:

198

W języku, który wykonuje pracę przez większość czasu, jest już zawarte zdanie foreach.

Nie chciałbym widzieć następujących rzeczy:

list.ForEach( item =>
{
    item.DoSomething();
} );

Zamiast:

foreach(Item item in list)
{
     item.DoSomething();
}

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ą:

  • Sprawdzanie typu: foreach odbywa się w czasie wykonywania, ForEach () jest w czasie kompilacji (Big Plus!)
  • Składnia wywołania delegata jest w rzeczywistości o wiele prostsza: objects.ForEach (DoSomething);
  • ForEach () można powiązać: chociaż zło / użyteczność takiej funkcji jest otwarta do dyskusji.

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.

Moneta
źródło
39
Dyskusja tutaj daje odpowiedź: forums.microsoft.com/MSDN/… Zasadniczo podjęto decyzję o utrzymaniu metod rozszerzeń funkcjonalnie „czystych”. ForEach zachęcałby do skutków ubocznych przy stosowaniu metod rozszerzenia, co nie było zamierzone.
mancaus
17
„foreach” może wydawać się wyraźniejsze, jeśli masz tło C, ale wewnętrzna iteracja jaśniej określa twoje zamiary. Oznacza to, że możesz robić takie rzeczy jak „sekwencja.AsParallel (). ForEach (...)”.
Jay Bazuzi
10
Nie jestem pewien, czy twoje zdanie na temat sprawdzania typu jest poprawne. foreachjest 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 hipotetyczna ForEachmetoda rozszerzenia.
Richard Poole,
49
Metoda rozszerzenie byłoby bardzo przydatne w łańcuchu LINQ z notacją kropka, jak: col.Where(...).OrderBy(...).ForEach(...). Znacznie prostsza składnia, brak zanieczyszczenia ekranu dzięki redundantnym zmiennym lokalnym lub długim nawiasom kwadratowym w foreach ().
Jacek Gorgoń
22
„Nie chciałbym widzieć, co następuje” - pytanie nie dotyczyło twoich osobistych preferencji, a ty sprawiłeś, że kod był bardziej nienawistny, dodając obce linie i obce nawiasy klamrowe. Nie jest tak nienawistna 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ńcuch in, a operację wykonać w bloku, co jest znacznie mniej naturalne lub spójne.
Jim Balter
73

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ę

public static void ForEach<T>(
    this IEnumerable<T> source,
    Action<T> action)
{
    foreach (T element in source) 
        action(element);
}
aku
źródło
11
Metody rozszerzenia pozwalają na to. Jeśli istnieje już implementacja instancji dla danej nazwy metody, wówczas metoda ta jest używana zamiast metody rozszerzenia. Możesz więc zaimplementować metodę rozszerzenia ForEach i nie kolidować z metodą List <>. ForEach.
Cameron MacFarland,
3
Cameron, uwierz mi, wiem, jak działają metody rozszerzeń :) Lista dziedziczy IEnumerable, więc nie będzie to oczywiste.
aku
9
Z Przewodnika programowania metod metod rozszerzenia ( msdn.microsoft.com/en-us/library/bb383977.aspx ): Metoda rozszerzenia o tej samej nazwie i podpisie co metoda interfejsu lub klasy nigdy nie zostanie wywołana. Wydaje mi się to dość oczywiste.
Cameron MacFarland
3
Czy mówię coś innego? Myślę, że nie jest dobrze, gdy wiele klas ma metodę ForEach, ale niektóre z nich mogą działać inaczej.
aku
5
Jedną ciekawą rzeczą do zapamiętania na temat List <>. ForEach i Array.ForEach jest to, że tak naprawdę nie używają wewnętrznie konstrukcji foreach. Oba używają zwykłego pętli dla. Może to kwestia wydajności ( diditwith.net/2006/10/05/PerformanceOfForeachVsListForEach.aspx ). Ale biorąc pod uwagę, że zarówno Array, jak i List implementują metody ForEach, zaskakujące jest to, że nie zaimplementowały one przynajmniej metody rozszerzenia dla IList <>, jeśli nie także dla IEnumerable <>.
Matt Dotson
53

Możesz napisać tę metodę rozszerzenia:

// Possibly call this "Do"
IEnumerable<T> Apply<T> (this IEnumerable<T> source, Action<T> action)
{
    foreach (var e in source)
    {
        action(e);
        yield return e;
    }
}

Plusy

Pozwala na łączenie:

MySequence
    .Apply(...)
    .Apply(...)
    .Apply(...);

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:

// possibly call this "Realize"
IEnumerable<T> Done<T> (this IEnumerable<T> source)
{
    foreach (var e in source)
    {
        // do nothing
        ;
    }

    return source;
}

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.

Jay Bazuzi
źródło
1
Pierwszej funkcji można użyć bez metody „realizuj”, jeśli jest napisana w następujący sposób:{foreach (var e in source) {action(e);} return source;}
jjnguy
3
Czy nie możesz po prostu użyć Select zamiast metody Apply? Wydaje mi się, że zastosowanie akcji do każdego elementu i uzyskanie go jest dokładnie tym, co robi Select. Np.numbers.Select(n => n*2);
rasmusvhansen
1
Myślę, że Apply jest bardziej czytelny niż użycie Select do tego celu. Czytając instrukcję Select, czytam ją jako „weź coś z wnętrza”, gdzie jako Apply jest „trochę popracować”.
Dr Rob Lang
2
@rasmusvhansen Właściwie Select()mówi , że stosuje funkcję do każdego elementu i zwraca wartość funkcji, w której Apply()mówi, że stosuje się Actiondo każdego elementu i zwraca oryginalny element.
NetMage
1
Jest to równoważne zSelect(e => { action(e); return e; })
Jim Balter,
35

Dyskusja tutaj daje odpowiedź:

Właściwie konkretna dyskusja, której byłem świadkiem, w rzeczywistości zależała od czystości funkcjonalnej. W wyrażeniu często przyjmuje się założenia, że ​​nie ma skutków ubocznych. Posiadanie ForEach to raczej zachęcanie do skutków ubocznych niż tylko znoszenie ich. - Keith Farmer (partner)

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.

mancaus
źródło
6
Zobacz także blogs.msdn.com/b/ericlippert/archive/2009/05/18/… . Ddoing narusza więc zasady programowania funkcjonalnego, na których opierają się wszyscy inni operatorzy sekwencji. Najwyraźniej jedynym celem wywołania tej metody jest wywołanie skutków ubocznych
Michael Freidgeim
7
Takie rozumowanie jest całkowicie nieprawdziwe. Przypadek użycia jest to, że potrzebne są skutki uboczne ... bez ForEach, trzeba napisać pętlę foreach. Istnienie ForEach nie sprzyja efektom ubocznym, a jego brak nie zmniejsza.
Jim Balter,
Biorąc pod uwagę, jak łatwo jest napisać metodę rozszerzenia, nie ma dosłownie powodu, że nie jest to standard językowy.
Kapitan Prinny,
17

Chociaż zgadzam się, że lepiej jest używać wbudowanej foreachkonstrukcji w większości przypadków, uważam, że użycie tej odmiany rozszerzenia ForEach <> jest nieco przyjemniejsze niż samodzielne zarządzanie indeksem foreach:

public static int ForEach<T>(this IEnumerable<T> list, Action<int, T> action)
{
    if (action == null) throw new ArgumentNullException("action");

    var index = 0;

    foreach (var elem in list)
        action(index++, elem);

    return index;
}
Przykład
var people = new[] { "Moe", "Curly", "Larry" };
people.ForEach((i, p) => Console.WriteLine("Person #{0} is {1}", i, p));

Dałby ci:

Person #0 is Moe
Person #1 is Curly
Person #2 is Larry
Chris Zwiryk
źródło
Świetne rozszerzenie, ale czy możesz dodać trochę dokumentacji? Jakie jest zamierzone wykorzystanie zwróconej wartości? Dlaczego indeks var jest raczej intem? Przykładowe użycie?
Mark T
Znak: zwracana wartość to liczba elementów na liście. Indeks jest specjalnie wpisywany jako int, dzięki niejawnemu pisaniu.
Chris Zwiryk
@Chris, w oparciu o powyższy kod, zwracana wartość jest zerowym indeksem ostatniej wartości na liście, a nie liczbą elementów na liście.
Eric Smith
@Chris, nie, nie jest: indeks jest zwiększany po pobraniu wartości (przyrost po postfiksie), więc po przetworzeniu pierwszego elementu indeks będzie wynosił 1 i tak dalej. Tak więc zwracana wartość to tak naprawdę liczba elementów.
MartinStettner
1
@MartinStettner komentarz Erica Smitha z 5/16 pochodzi z wcześniejszej wersji. Poprawił kod 5/18, aby zwrócić poprawną wartość.
Michael Blackburn
16

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.

Jay Bazuzi
źródło
1
a) To nie jest nawet zdalna odpowiedź na pytanie. b) Ta odpowiedź byłaby wyraźniejsza, gdyby z góry wspomniała, że ​​chociaż nie ma Enumerable.ForEach<T>metody, istnieje List<T>.ForEachmetoda. 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); }
Jim Balter
13

Zawsze się nad tym zastanawiałem, dlatego zawsze noszę to ze sobą:

public static void ForEach<T>(this IEnumerable<T> col, Action<T> action)
{
    if (action == null)
    {
        throw new ArgumentNullException("action");
    }
    foreach (var item in col)
    {
        action(item);
    }
}

Fajna mała metoda rozszerzenia.

Aaron Powell
źródło
2
col może być zerowy ... możesz to również sprawdzić.
Amy B,
Tak, sprawdzam w bibliotece, po prostu tęskniłem, gdy robiłem to szybko tam.
Aaron Powell,
Interesujące jest dla mnie to, że wszyscy sprawdzają parametr akcji pod kątem wartości null, ale nigdy parametr source / col.
Cameron MacFarland
2
Interesujące jest dla mnie to, że wszyscy sprawdzają parametry pod kątem wartości null, gdy nie sprawdzają również wyników w wyjątkach ...
o
Ani trochę. Wyjątek Null Ref nie podałby nazwy parametru, który był przyczyną błędu. I możesz również sprawdzić w pętli, aby rzucić konkretną wartość zerową, mówiąc, że kolumna [indeks #] jest pusta z tego samego powodu
Roger Willcocks
8

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:

collection.Where(i => i.Name = "hello").Select(i => i.FullName);

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.

Scott Dorman
źródło
7

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)

IEnumerable<string> people = new List<string>(){"alica", "bob", "john", "pete"};
people.Select(p => { Console.WriteLine(p); return p; });

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:

static IEnumerable<T> WithLazySideEffect(this IEnumerable<T> src, Action<T> action) {
  return src.Select(i => { action(i); return i; } );
}

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.

Martijn
źródło
Fantastyczne, nigdy nie kliknęło, że zwrócony wybór będzie działał w ten sposób. Przyjemne użycie składni metody, ponieważ nie sądzę, że można to zrobić za pomocą składni wyrażenia (stąd wcześniej nie myślałem o tym w ten sposób).
Alex KeySmith
@AlexKeySmith Mógłbyś to zrobić ze składnią wyrażenia, gdyby C # miał operator sekwencji, podobnie jak jego przodek. Ale cały sens ForEach polega na tym, że wykonuje akcję typu void i nie można używać typów void w wyrażeniach w języku C #.
Jim Balter,
imho, teraz Selectjuż 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ę.
Matthias Burger,
@MatthiasBurger Jeśli Selectnie zrobi tego, co powinien, to jest problem z implementacją Select, a nie z wywoływanym kodem Select, który nie ma wpływu na to, co Selectrobi.
Martijn,
4

@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ę

Action<blah,blah> f = { foo };

List1.ForEach(p => f(p))
List2.ForEach(p => f(p))

itp...

Logika jest w jednym miejscu i nie zanieczyściłeś swojej klasy.

spoulson
źródło
foreach (var p w List1.Concat (List2) .Concat (List3)) {... robić rzeczy ...}
Cameron MacFarland
Jeśli to takie proste f(p), a następnie pozostawić na zewnątrz { }do skrótów: for (var p in List1.Concat(List2).Concat(List2)) f(p);.
spoulson
3

Większość metod rozszerzenia LINQ zwraca wyniki. ForEach nie pasuje do tego wzoru, ponieważ nic nie zwraca.

leppie
źródło
2
ForEach używa akcji <T>, która jest kolejną formą delegowania
Aaron Powell,
2
Z wyjątkiem prawa leppie, wszystkie metody rozszerzenia Enumerable zwracają wartość, więc ForEach nie pasuje do wzorca. Delegaci nie wchodzą w to.
Cameron MacFarland,
Tyle, że metoda rozszerzenia nie musi zwracać wyniku, służy dodaniu sposobu wywoływania często używanej operacji
Aaron Powell,
1
Rzeczywiście, Slace. A co jeśli nie zwróci wartości?
TraumaPony
1
@Martijn iepublic IEnumerable<T> Foreach<T>(this IEnumerable<T> items, Action<T> action) { /* error check */ foreach (var item in items) { action(item); yield return item; } }
McKay,
3

Jeśli masz F # (który będzie w następnej wersji .NET), możesz użyć

Seq.iter doSomething myIEnumerable

TraumaPony
źródło
9
Dlatego Microsoft postanowił nie udostępniać metody ForEach dla IEnumerable w C # i VB, ponieważ nie byłaby ona czysto funkcjonalna; ale NIE Dali jej (iter nazwy) w swoim bardziej funkcjonalnym języku, F #. Ich logika nawiązuje do mnie.
Scott Hutchinson
3

Częściowo dzieje się tak, ponieważ projektanci języków nie zgadzają się z tym z filozoficznego punktu widzenia.

  • Brak posiadania (i testowanie ...) funkcji jest mniej pracy niż posiadanie funkcji.
  • Nie jest tak naprawdę krótszy (istnieją pewne przypadki funkcji przechodzących, ale nie byłoby to podstawowe zastosowanie).
  • Jego celem jest wywoływanie skutków ubocznych, a nie o to chodzi w Linq.
  • Dlaczego mamy inny sposób na zrobienie tego samego, co funkcja, którą już mamy? (każde słowo kluczowe)

https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/

McKay
źródło
2

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.

Paco
źródło
a) To nie jest odpowiedź na pytanie. b) ToList wymaga dodatkowego czasu i pamięci.
Jim Balter
2

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

Kirill Osenkov
źródło
czy to kiedykolwiek zostało wdrożone? Nie sądzę, ale czy jest jakiś inny adres URL, który zastępuje ten bilet? MS Connect został przerwany
Knocte
To nie zostało zaimplementowane. Rozważ złożenie nowego wydania na github.com/dotnet/runtime/issues
Kirill Osenkov,
2

Czy to ja, czy lista <T>. Poszukiwania zostały prawie całkowicie przestarzałe przez Linq. Pierwotnie było

foreach(X x in Y) 

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

IEnumerator<int> enumerator = list.GetEnumerator();
while (enumerator.MoveNext())
{
    int i = enumerator.Current;

    Console.WriteLine(i);
}

(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/

użytkownik18784
źródło
1
Uważam, że wygenerowany kod dla „foreach” powinien mieć blok próbny. Ponieważ IEnumerator T dziedziczy z interfejsu IDisposable. Zatem w wygenerowanym bloku ostatecznie IEnumerator instancji T powinien zostać usunięty.
Morgan Cheng
2

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:

private static IEnumerable<T> ForEach<T>(IEnumerable<T> xs, Action<T> f) {
    foreach (var x in xs) {
        f(x); yield return x;
    }
}
Fredefox
źródło
1
To to samo coSelect(x => { f(x); return x;})
Jim Balter,
0

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.

Wiewiórka
źródło
5
Czy to naprawdę tak jest? Nie widzę, jak foreachma mniej sprawdzania czasu kompilacji. Jasne, jeśli twoja kolekcja nie jest ogólną IEnumerable, zmienna pętli będzie zmienna object, ale to samo byłoby prawdą w przypadku hipotetycznej ForEachmetody rozszerzenia.
Richard Poole,
1
Jest to wspomniane w zaakceptowanej odpowiedzi. Jest to jednak po prostu źle (a Richard Poole powyżej ma rację).
Jim Balter