如何确保序列具有一定的长度?

guh*_*hou 22 c# linq extension-methods

我要检查一个IEnumerable包含正好一个元素.这个片段确实有效:

bool hasOneElement = seq.Count() == 1
Run Code Online (Sandbox Code Playgroud)

然而,它并不是非常有效,因为Count()它将枚举整个列表.显然,知道列表是空的或包含多于1个元素意味着它不是空的.是否存在具有此短路行为的扩展方法?

Jon*_*eet 20

这应该这样做:

public static bool ContainsExactlyOneItem<T>(this IEnumerable<T> source)
{
    using (IEnumerator<T> iterator = source.GetEnumerator())
    {
        // Check we've got at least one item
        if (!iterator.MoveNext())
        {
            return false;
        }
        // Check we've got no more
        return !iterator.MoveNext();
    }
}
Run Code Online (Sandbox Code Playgroud)

可以进一步忽视这一点,但我不建议你这样做:

public static bool ContainsExactlyOneItem<T>(this IEnumerable<T> source)
{
    using (IEnumerator<T> iterator = source.GetEnumerator())
    {
        return iterator.MoveNext() && !iterator.MoveNext();
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一种时髦的技巧,但可能不应该在生产代码中使用.这还不够清楚.事实上,在RHS工作正常的情况下,&&运算符的LHS中的副作用是令人讨厌的......虽然很有趣;)

编辑:我刚刚看到你提出完全相同的东西,但任意长度.你的最终回复陈述是错误的 - 它应该是回归!en.MoveNext().这是一个完整的方法,具有更好的名称(IMO),参数检查和优化ICollection/ ICollection<T>:

public static bool CountEquals<T>(this IEnumerable<T> source, int count)
{
    if (source == null)
    {
        throw new ArgumentNullException("source");
    }
    if (count < 0)
    {
        throw new ArgumentOutOfRangeException("count",
                                              "count must not be negative");
    }
    // We don't rely on the optimizations in LINQ to Objects here, as
    // they have changed between versions.
    ICollection<T> genericCollection = source as ICollection<T>;
    if (genericCollection != null)
    {
        return genericCollection.Count == count;
    }
    ICollection nonGenericCollection = source as ICollection;
    if (nonGenericCollection != null)
    {
        return nonGenericCollection.Count == count;
    }
    // Okay, we're finally ready to do the actual work...
    using (IEnumerator<T> iterator = source.GetEnumerator())
    {
        for (int i = 0; i < count; i++)
        {
            if (!iterator.MoveNext())
            {
                return false;
            }
        }
        // Check we've got no more
        return !iterator.MoveNext();
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:现在对于功能性粉丝来说,是一种递归形式CountEquals(请不要使用它,它只是在这里咯咯笑):

public static bool CountEquals<T>(this IEnumerable<T> source, int count)
{
    if (source == null)
    {
        throw new ArgumentNullException("source");
    }
    if (count < 0)
    {
        throw new ArgumentOutOfRangeException("count", 
                                              "count must not be negative");
    }
    using (IEnumerator<T> iterator = source.GetEnumerator())
    {
        return IteratorCountEquals(iterator, count);
    }
}

private static bool IteratorCountEquals<T>(IEnumerator<T> iterator, int count)
{
    return count == 0 ? !iterator.MoveNext()
        : iterator.MoveNext() && IteratorCountEquals(iterator, count - 1);
}
Run Code Online (Sandbox Code Playgroud)

编辑:请注意,对于像LINQ to SQL这样的东西,你应该使用简单的Count()方法 - 因为这将允许它在数据库中完成而不是在获取实际结果之后.

  • LOL @Jon为他的`return iterator.MoveNext()&&!iterator.MoveNext(); `.我的答案是"两个"! (3认同)
  • +1 iterator.MoveNext()&&!iterator.MoveNext()哇! (2认同)

Ani*_*Ani 7

不,但你可以自己写一个:

 public static bool HasExactly<T>(this IEnumerable<T> source, int count)
 {
   if(source == null)
      throw new ArgumentNullException("source");

   if(count < 0)
      return false;

   return source.Take(count + 1).Count() == count;
 }
Run Code Online (Sandbox Code Playgroud)

编辑:在澄清之后从至少 改为完全改变.

为了更全面和有效的解决方案(只使用1枚举并检查序列实现ICollectionICollection<T>在这种情况下枚举是没有必要的),你可能想看看我的答案在这里,它可以让你指定你是否正在寻找Exact,AtLeastAtMost测试.