2009-05-05 11 views
111

Niedawno zacząłem od LINQ i jego niesamowity. Zastanawiałem się, czy LINQ pozwoli mi zastosować funkcję - dowolną funkcję - do wszystkich elementów kolekcji, bez używania foreach. Coś w rodzaju funkcji python lambda.Zastosuj funkcję do wszystkich elementów kolekcji poprzez LINQ

Na przykład jeśli mam listę int, mogę dodać stałą do każdego elementu przy użyciu LINQ

Jeśli mam tabeli DB, mogę ustawić pola dla wszystkich rekordów z wykorzystaniem LINQ.

Używam C#

Odpowiedz

123

Typowym sposobem podejścia jest dodanie własnej metody ForEach na . Oto jeden mamy w MoreLINQ:

public static void ForEach<T>(this IEnumerable<T> source, Action<T> action) 
{ 
    source.ThrowIfNull("source"); 
    action.ThrowIfNull("action"); 
    foreach (T element in source) 
    { 
     action(element); 
    } 
} 

(Gdzie ThrowIfNull jest metoda rozszerzenie na wszelkiego rodzaju odniesienia, który robi to oczywistą rzeczą.)

To będzie interesujące zobaczyć, czy jest to część .NET 4.0. Jest to sprzeczne ze stylem funkcjonalnym LINQ, ale nie ma wątpliwości, że wiele osób uważa to za przydatne.

Gdy masz, że można pisać takie rzeczy jak:

people.Where(person => person.Age < 21) 
     .ForEach(person => person.EjectFromBar()); 
+11

Nigdy tak naprawdę nie rozumiałem, że ludzie muszą to robić. Pętla foreach jest bardziej czytelna dla moich oczu; oddzielenie części "funkcjonalnej", która pobiera dane i działającej na nich "akcji" pomaga wyjaśnić strukturę algorytmów. – mquander

+12

Tak, ogólnie wolę pętlę foreach. Z drugiej strony, jeśli już otrzymałeś delegata do złożenia wniosku do każdego elementu, nie jestem pewien, czy można go nazwać delegatem w wyraźnej pętli foreach. Nie używałbym tego często, ale tylko czasami wydaje mi się to dobrym rozwiązaniem. –

+1

Bardzo przydatne, dziękuję :) –

71

The idiomatyczne sposób to zrobić z LINQ jest przetwarzanie zbierania i powrócić nową kolekcję odwzorowany w sposób chcesz. Na przykład, aby dodać stałą do każdego elementu, że chcesz coś

var newNumbers = oldNumbers.Select(i => i + 8); 

Robi to w sposób funkcjonalny zamiast mutacji stanu istniejącego zbioru często pomaga oddzielić różne operacje w sposób, który jest zarówno łatwiejsze do odczytania i łatwiejsze do zrozumienia przez kompilator.

Jeśli jesteś w sytuacji, w której chcesz zastosować akcję do każdego elementu kolekcji (akcja z efektami ubocznymi, które nie są związane z rzeczywistą zawartością kolekcji), to nie jest to, co najlepiej pasuje do LINQ na przykład, chociaż można go sfałszować za pomocą Select (lub napisać własną metodę rozszerzenia IEnumerable, jak wielu ludzi.) W tym przypadku najlepiej jest trzymać się pętli foreach.

+2

Nie wspominając już o mutowaniu kolekcji podczas iteracji nad nią jest niebezpieczne, niemożliwe, w zależności od języka. – Soviut

+8

W języku C# mutowanie elementów kolekcji jest dozwolone, ale dodanie lub usunięcie kolekcji może zakończyć się niepowodzeniem lub spowodować niepożądane wyniki. – mquander

+0

użycie metody Wybierz naprawdę pomaga tylko w niektórych typach, takich jak int. Co jeśli chcesz ustawić właściwość obiektów na liście na pewną wartość? czy nie byłoby prostsze i bardziej skalowalne w użyciu .ForEach()? – andy

25

haha, człowieku, ja po prostu zadał to pytanie kilka dni temu (niby) ... spróbuj tego:

przykład:

someIntList.ForEach(i=>i+5); 

ForEach() jest jednym z wbudowanych metod .NET

Spowoduje to zmianę listy, a nie zwrócenie nowej.

+9

Należy zauważyć, że działa to tylko na rzeczywistych kolekcjach List , chyba że zdefiniowałeś własną metodę ForEach, aby to zrobić. – mquander

+0

Tak, myślę, że zakładam, że chcesz edytować obiekt, który już masz w pamięci, ale tak, masz rację. nigdy nie zakładaj! – andy

+3

Wierzę, że mquander odwoływał się do faktu, że ForEach nie jest zdefiniowany na IEnumerable , tylko List Odrade

1

Można spróbować coś

var foo = (from fooItems in context.footable select fooItems.fooID + 1); 

Zwraca listę identyfikatorów +1, można zrobić to samo z używanie funkcji do tego, co masz w klauzuli select.

Aktualizacja: Jak sugeruje Jon Skeet jest to lepsza wersja fragment kodu po prostu napisali:

var foo = context.footable.Select(foo => foo.fooID + 1); 
+1

Proponuję, aby w przypadkach, w których wystarczy wykonać prostą projekcję, wyrażeń zapytania są na górze. Użyłbym "var foo = context.footable.Select (foo => foo.fooID + 1);" –

+0

Widzę, wciąż stosunkowo nowy w Linq, wciąż uczący się, kiedy idę. Dziękujemy za udzielenie porady – Drahcir

35

Można także rozważyć wyjście równoległe, zwłaszcza jeśli nie dbają o sekwencji i więcej o uzyskanie coś zrobić dla każdej pozycji:

SomeIEnumerable<T>.AsParallel().ForAll(Action<T>/Delegate/Lambda) 

na przykład:

var numbers = new[] { 1, 2, 3, 4, 5 }; 
numbers.AsParallel().ForAll(Console.WriteLine); 

HTH.

+1

+1 za wyświetlenie opcji .AsParallel –

+1

Należy uważać na AsParallel(). ForAll(), ponieważ powoduje nieprzewidywalny wynik. Na przykład mam przycisk do wykonania tego kodu po kliknięciu: myEnumerable.AsParallel(). ForAll (i as string => otherDictionary.Add (i, 0)). Doda null jako klucz do otherDictionary. Musiałem przerobić, aby użyć pętli foreach. Dziwne. – YukiSakura

+2

@YukiSakura Niezupełnie AsParallel() .Alarm ForAll() - Bity, które są niebezpieczne, używają współdzielonych stwierdzeń między wieloma wątkami, a następnie obsada "i as string" jest możliwą przyczyną podania klucza pustego. Chcesz udostępnić pełniejszą próbkę swojego kodu, aby uzyskać informacje zwrotne na temat poprawy? – Jaans

-1

Znalazłem jakiś sposób, aby wykonać w dniu słowniku zawierać moje niestandardowe metody klasy

foreach (var item in this.Values.Where(p => p.IsActive == false)) 
      item.Refresh(); 

Gdzie ta "pochodzi z: Słownik < strun MyCustomClass >

class MyCustomClass 
{ 
    public void Refresh(){} 
} 
5

Dla kolekcji, które NIE obsługa ForEach można użyć statycznej metody ForEach w Parallel klasa:

var options = new ParallelOptions() { MaxDegreeOfParallelism = 1 }; 
Parallel.ForEach(_your_collection_, options, x => x._Your_Method_()); 
10

Albo możesz to zhackować.

Items.All(p => { p.IsAwesome = true; return true; }); 
+1

Czasami włamanie jest rzeczywiście konieczne;) –

+5

To jest hakowanie "Wszystkie", aby zmienić go na "ForEach". Mam złe przeczucie, że wprowadzi to w błąd początkującego myślenie, że "Wszystko" oznacza "Wykonaj we wszystkich żywiołach". – ToolmakerSteve

Powiązane problemy