2017-07-07 99 views
5

Byłem przyzwyczajony do używania metody Javy Stream#Peek, ponieważ jest to przydatna metoda do debugowania operacji strumienia pośredniego. Dla tych z Was, którzy nie są zaznajomieni ze sposobem Stream#Peek, poniżej przedstawiono definicję nim:Jaki jest odpowiednik metody Stream # Peek w języku Java w Linq C#?

Stream<T> peek(Consumer<? super T> action)

zwraca strumień składający się z elementów tego strumienia, dodatkowo wykonując przewidziane działania na każdym elemencie jako elementy są zużywane z wynikowego strumienia. Jest to operacja pośrednia .

Rozważmy prosty przykład poniżej:

List<Integer> integerList = Arrays.asList(1,2,3,4,5,6,7,8,9,10); 
List<Integer> result = integerList.stream() 
            .filter(i -> i % 2 == 0) 
            .peek(System.out::println) 
            .collect(Collectors.toList()); 

Dzięki metodzie Stream#Peek, to powinien w zasadzie pozwala mi wydrukować wszystkie parzyste numery do konsoli, dzięki czemu można przetestować, aby sprawdzić, czy to, co Oczekiwałbym.

starałem się znaleźć odpowiedź na pytanie pod ręką, ale nie wydaje się znaleźć podobną metodę w C#, czy ktoś wie równowartość Java Stream#Peek lub jakiś inny sposób z podobnym zachowaniem?

+0

Istnieje metoda ForEach na klasy List (jest jakaś debata o tym, dlaczego nie jest on dostępny dla IEnumerable), więc trzeba by nazwać „ToList” przed użyciem. Chociaż zwraca nieważne, a nie kolejną listę. Ale łatwo jest napisać własną metodę rozszerzenia dla IEnumerable, która to robi. – user1242967

+0

@ user1242967 Nie ma * debaty * o tym, dlaczego nie istnieje dla 'IEnumerable'. Toczy się debata, czy byłby to dobry pomysł dla "IEnountable", ale dlaczego nie został jasno określony i nie jest kwestionowany. – Servy

+0

@Servy Tak, złe sformułowanie z mojej strony. – user1242967

Odpowiedz

5

Nie istnieje odpowiednik Peek w LINQ - to znaczy nie istnieje metoda, która wykonuje pewne elementy działanie i powraca źródłowych. Istnieje metoda ForEach w klasie , która wykonuje operację na każdym elemencie, ale nie zwraca elementów źródłowych i jak już wspomniano, nie jest to rozszerzenie IEnumerable.

Ale można łatwo pisać własne rozszerzenie

public static IEnumerable<T> Peek<T>(this IEnumerable<T> source, Action<T> action) 
{ 
    if (source == null) throw new ArgumentNullException(nameof(source)); 
    if (action == null) throw new ArgumentNullException(nameof(action)); 

    return Iterator(); 

    IEnumerable<T> Iterator() // C# 7 Local Function 
    { 
     foreach(var item in source) 
     { 
      action(item); 
      yield return item; 
     } 
    } 
} 
+1

Myślę, że deklarowanie funkcji lokalnej tylko w celu jej wykonania i zwrócenia wyników jest zbędne. Otrzymasz ten sam efekt za pomocą mniejszego kodu za pomocą bloku foreach. –

+1

@MattJohnson i jak sprawdzanie parametrów będzie działało z odroczonym wykonaniem? –

+0

Odroczone wykonanie uzyskuje się przez użycie zwrotu zwrotu - nie przez wywołanie funkcji. Wciąż otrzymujesz odroczone wykonanie, tak jak jest napisane, i jeśli wbudujesz funkcję. (Zwróć uwagę, że w ogóle nie zwracasz tej funkcji, po prostu ją wykonujesz.) –

1

EDYCJA: ta odpowiedź jest nieaktualna, po edycji na oryginalne pytanie. Początkowe pytanie było sformułowane z intencją peek -ing wszystkie filter wartościami -ed, a następnie wykonując findFirst:

List<Integer> integerList = Arrays.asList(1,2,3,4,5,6,7,8,9,10); 
Optional<Integer> result = integerList.stream() 
             .filter(i -> i % 2 ==0== 0) 
             .peek(System.out::println) 
             .findFirst(); 

O ile mi wiadomo, nie ma wbudowaną LINQ rozwiązanie to zrobić, tak oto metodę rozszerzenia:

public static IEnumerable<T> Peek<T>(this IEnumerable<T> source, Action<T> action) 
{ 
    using (var iterator = source.GetEnumerator()) 
    { 
     while (iterator.MoveNext()) 
     { 
      action(iterator.Current); 
     } 
    } 

    using (var iterator = source.GetEnumerator()) 
    { 
     while (iterator.MoveNext()) 
     { 
      yield return iterator.Current; 
     } 
    } 
} 

Zastosowanie:

var integerList = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 
int? result = integerList.Where(i => i % 2 == 0) 
         .Peek(i => Console.WriteLine(i)) 
         .FirstOrDefault(); 

Console.WriteLine(result); 

wyjściowa:

2 
4 
6 
8 
10 
2 
4

Można użyć Select z Statement Lambda do tego.Rozważmy:

var integerList = new List<int> {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 
var result = integerList 
    .Where(i => i % 2 == 0) 

    // this select uses a statement lambda 
    .Select(i => 
    { 
     Console.WriteLine(i); 
     return i; 
    }) 

    .Whatever(...);