Linq跳过第一个where条件(针对对象的Linq)

4

我需要使用LINQ查询跳过第一个符合谓词的元素。目前我所知道的最好方法是像这样:

var list = new [] {1,2,3,4,5,6,7,8,9};
var skippedFirstOdd = false;
var skipFirstEvenNumber = list.SkipWhile(number => 
                                         {
                                             if(number % 2 != 0)
                                             {
                                                 skippedFirst = true;
                                                 return true;
                                             }
                                             else
                                             {
                                                 return false;
                                             }
                                         });

我认为以下代码可以实现功能,但不够优雅。是否有更简洁的方法实现?

你想跳过第一个元素还是跳过所有元素直到第一个偶数?为什么要设置 skippedFirst 变量?你需要它吗? - Tomas Jansson
我想做的是返回所有元素,除了第一个匹配谓词的元素。 - ilivewithian
2
你能解释一下为什么“Skip”扩展方法不能做你想要的事吗?为什么不直接使用 sequence.Where(whatever).Skip(1)? - Eric Lippert
1
也许我对“跳过”功能的理解有误。我想获取系列中除谓词匹配的第一个元素外的所有元素。例如,如果列表是{2,2,1,3,5},我可能想要除了第一个奇数之外的所有元素,因此我会得到{2,2,3,5}。 - ilivewithian
1
明白了。你不想要“序列中所有与谓词匹配的元素,除了第一个”。你想要“序列中除了第一个与谓词匹配的元素之外的所有元素。”(这就是你说的——但这是一个潜在的棘手区别需要理解。) - Eric Lippert
1个回答

7
您可以编写一个迭代器块扩展方法:
public static IEnumerable<T> SkipFirstMatching<T>
      (this IEnumerable<T> source, Func<T, bool> predicate)
{        
    if (source == null)
        throw new ArgumentNullException("source");

    if (predicate == null)
        throw new ArgumentNullException("predicate");

    return SkipFirstMatchingCore(source, predicate);
}

private static IEnumerable<T> SkipFirstMatchingCore<T>
      (IEnumerable<T> source, Func<T, bool> predicate)
{            
    bool itemToSkipSeen = false;

    foreach (T item in source)
    {
        if (!itemToSkipSeen && predicate(item))
            itemToSkipSeen = true;

        else yield return item;
    }
}

并将其用作:

var list = new [] { 1,2,3,4,5,6,7,8,9 };
var skipFirstEvenNumber = list.SkipFirstMatching(i => i % 2 == 0);

顺便提一下,你当前的代码似乎完全不正确。变量名为skipFirstEvenNumber,但查询跳过了奇数。其次,你似乎试图使用副作用来使查询工作,但你只是设置了标志变量,而没有读取它。因此,你当前的代码应该像普通的SkipWhile一样工作。 编辑:使参数验证变得急切。

为什么需要将解决方案分成两个不同的方法? - Luc Morin
@LucMorin:所以参数验证可以是急切的,但查询本身可以延迟运行。 - Ani
这是一种已知的模式并有指导吗?还是很多人都做的非正式事情?如果你有相关的论文可以分享,那将会被阅读;-) - Luc Morin
1
参见:]https://codeblog.jonskeet.uk/2010/09/03/reimplementing-linq-to-objects-part-2-quot-where-quot/] 特别是,“我们刚刚碰到了C#中的一个设计缺陷。当您想在“立即”(通常用于验证)和“延迟”之间分割执行时,C#中的迭代器块根本无法正常工作。相反,我们必须将实现分成两个部分:一个用于验证的普通方法,然后调用迭代器方法进行延迟处理。” - Ani

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