在Linq C#中,Java的Stream#Peek方法的等效方法是什么?

11

我已经习惯了使用Java的Stream#Peek方法,因为它是一种有用的方法来调试中间流操作。对于那些不熟悉Stream#Peek方法的人,以下是其定义:

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

返回一个由此流的元素组成的流, 另外在从生成的流中消耗每个元素时执行提供的操作。这是一种中间操作。

考虑下面这个简单的例子:

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());

使用Stream#Peek方法,这应该基本上允许我打印所有偶数到控制台,以便我可以测试看看是否符合我的期望。
我尝试寻找答案来解决问题,但似乎在C#中找不到类似的方法,请问有谁知道Java中的Stream#Peek的等效方法或具有类似行为的其他方法吗?

@user1242967,关于为什么IEnumerable不存在这个问题是没有争议的。虽然是否为IEnumerable提供此功能存在争议,但为什么它不存在已经被清楚地阐述,并且不容置疑。 - Servy
@Servy 对不起,我措辞不当。 - user1242967
@Aominè,只是为了明确起见,这就是我的建议:ToList().ForEach(Console.Write) - user1242967
2
“使用Stream#Peek方法,基本上应该允许我将所有偶数打印到控制台,以便测试是否符合预期。”这个说法是不正确的。在您的示例中,peek只会打印findFirst的结果。如果你问我,这有点违反直觉,但我想这就是预期的行为。 - budi
@budi 不好意思,我会编辑帖子以适应那个语句。但是由于 findFirst 是一个短路终端操作,所以我之前说的不正确。我已经编辑了帖子来说明我的最初意图。 - Ousmane D.
显示剩余2条评论
3个回答

6

LINQ中没有Peek的等效方法,即没有一种可以执行某些操作并返回源元素的方法。 List类中有一个ForEach方法,它对每个元素执行操作,但它不返回源元素,并且如前所述,它不是IEnumerable扩展。

但是您可以轻松编写自己的扩展。

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
我认为声明一个本地函数只是为了执行它并返回结果是多余的。你可以通过仅使用foreach块来用更少的代码获得相同的效果。 - Matt Johnson-Pint
2
@MattJohnson,参数检查如何与延迟执行一起工作? - Sergey Berezovskiy
1
@MattJohnson 的问题是关于参数检查的。我会进一步澄清这个问题——当使用延迟执行时,参数检查会在什么时候发生? - Sergey Berezovskiy
2
等等,我错了...使用本地函数时,参数检查会提前发生。如果没有它,它将在枚举时发生...你是对的,我学到了东西! :) - Matt Johnson-Pint
2
@MattJohnson 是的,这就是LINQ方法在框架中实现的方式,但当然没有本地函数 - 只有每个迭代器的私有方法。 - Sergey Berezovskiy
显示剩余2条评论

6
您可以使用 Select语句 Lambda 来实现。请考虑以下代码:
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(...);

1

编辑:此答案已过时,原问题已进行编辑。最初的问题是带有peek操作所有filter值的意图,然后执行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();

据我所知,LINQ 中没有内置的解决方案来完成此操作,因此这里提供一个扩展方法:
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;
        }
    }
}

使用方法:

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);

输出:

2
4
6
8
10
2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接