C# Linq:前瞻性条件

C# Linq:前瞻性条件,c#,linq,C#,Linq,我有一个列表(简化) 我需要{E,L}->项,其中它们的种类==null,下一种也==null 假设有一个ID在按顺序递增 这种前瞻性在Linq是否可行?像这样 void Main() { List<SomeClass> list = new List<SomeClass>() { new SomeClass() { Kind = null, Name = "E" }, new SomeClass() { Kind = null,

我有一个列表(简化)

我需要{E,L}->项,其中它们的种类==null,下一种也==null

假设有一个ID在按顺序递增

这种前瞻性在Linq是否可行?

像这样

void Main()
{
    List<SomeClass> list = new List<SomeClass>() {
        new SomeClass() { Kind = null, Name = "E" },
        new SomeClass() { Kind = null, Name = "W" },
        new SomeClass() { Kind = 4, Name = "T" },
        new SomeClass() { Kind = 5, Name = "G" },
        ...
    };

    var query = list.Where ((s, i) =>
        !s.Kind.HasValue &&
        list.ElementAtOrDefault(i + 1) != null &&
        !list.ElementAt(i + 1).Kind.HasValue);
}

public class SomeClass
{
    public int? Kind { get; set; }
    public string Name { get; set; }
}
void Main()
{
列表=新列表(){
新建SomeClass(){Kind=null,Name=“E”},
新建SomeClass(){Kind=null,Name=“W”},
新建SomeClass(){Kind=4,Name=“T”},
新的SomeClass(){Kind=5,Name=“G”},
...
};
var query=list.Where((s,i)=>
!s.Kind.HasValue&&
list.ElementAtOrDefault(i+1)!=null&&
!list.ElementAt(i+1.Kind.HasValue);
}
公共类
{
公共int?种类{get;set;}
公共字符串名称{get;set;}
}
编辑:窃取@Jeff Marcado的解决方案,以实现与上述使用类似的扩展方法,但更简洁,并且不让您处理索引:

public static IEnumerable<TSource> WhereWithLookahead<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, bool> predicate) where TSource : class
{
    using(var enumerator = source.GetEnumerator())
    {
        if (!enumerator.MoveNext())
        {
            //empty
            yield break;
        }

        var current = enumerator.Current;
        while (enumerator.MoveNext())
        {
            var next = enumerator.Current;

            if(predicate(current, next))
            {
                yield return current;
            }

            current = next;
        }

        if (predicate(current, null))
        {
            yield return current;
        }

    }
}

// Use:
var query2 = list.WhereWithLookahead((current, next) =>
    !current.Kind.HasValue &&
    (next != null) &&
    next.Kind.HasValue);
公共静态IEnumerable WherewhithLookahead(此IEnumerable源代码,Func谓词),其中TSource:class
{
使用(var enumerator=source.GetEnumerator())
{
如果(!enumerator.MoveNext())
{
//空的
屈服断裂;
}
var current=枚举数。当前值;
while(枚举数.MoveNext())
{
var next=枚举数.Current;
if(谓词(当前,下一个))
{
产生回流电流;
}
当前=下一个;
}
if(谓词(当前,空))
{
产生回流电流;
}
}
}
//使用:
var query2=列表。其中向前看((当前,下一个)=>
!current.Kind.HasValue&&
(下一步!=null)&&
next.Kind.HasValue);

对于函数方法,可以实现如下所示的前瞻枚举器:

IEnumerable<Item> collection = ...;
var lookahead = collection.Zip(collection.Skip(1), Tuple.Create);
不幸的是,这将是非常低效的。您要枚举集合的长度两次,这可能非常昂贵

最好为此编写自己的枚举器,这样您只需一次遍历集合:

public static IEnumerable<TResult> LookAhead<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, TSource, TResult> selector)
{
    if (source == null) throw new ArugmentNullException("source");
    if (selector == null) throw new ArugmentNullException("selector");

    using (var enumerator = source.GetEnumerator())
    {
        if (!enumerator.MoveNext())
        {
            //empty
            yield break;
        }
        var current = enumerator.Current;
        while (enumerator.MoveNext())
        {
            var next = enumerator.Current;
            yield return selector(current, next);
            current = next;
        }
    }
}

我在您的解决方案中看到索引超出范围异常:如果最后一项
Kind
null
,则
list[I+1]
将过度索引列表。仍然不完美:将
I
替换为
elementatorderfault
中的
I+1
ElementAt
中的
,我不确定它的效率。
ElementAtOrDefault
ElementAt
调用都将再次遍历列表(
O(n)
每个调用,而不是
O(1)
),这将花费更多的时间。如果性能是一个问题,我会避免在这种情况下使用LINQ,而采用更标准的方法。@Lester:
ElementAt
ElementAtOrDefault
方法仅在未实现
IList
的情况下迭代所有元素。如果他们实现了
IList
接口,那么该项将以固定时间返回(
O(1)
)。@Lester:任何其他方法都将使用此处提供的相同索引,因此用您喜欢的任何其他方法替换
ElementAtOrDefault
,解决方案仍然适用。我确实同意,在这里使用LINQ仅仅是为了它可能不是最灵活/可读的,但它是可以做到的。酷。希望你不介意我在这里偷了一些基本想法来更新我的答案:)
var query = collection.Zip(collection.Skip(1), Tuple.Create)
    .Where(tuple => tuple.Item1.Kind == null && tuple.Item2.Kind == null)
    .Select(tuple => tuple.Item1);
public static IEnumerable<TResult> LookAhead<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, TSource, TResult> selector)
{
    if (source == null) throw new ArugmentNullException("source");
    if (selector == null) throw new ArugmentNullException("selector");

    using (var enumerator = source.GetEnumerator())
    {
        if (!enumerator.MoveNext())
        {
            //empty
            yield break;
        }
        var current = enumerator.Current;
        while (enumerator.MoveNext())
        {
            var next = enumerator.Current;
            yield return selector(current, next);
            current = next;
        }
    }
}
var query = collection.LookAhead(Tuple.Create)
    .Where(tuple => tuple.Item1.Kind == null && tuple.Item2.Kind == null)
    .Select(tuple => tuple.Item1);