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

时间:2010-09-27 08:10:06

标签: c# linq extension-methods

我想检查IEnumerable是否包含 完全 一个元素。这段代码确实有效:

bool hasOneElement = seq.Count() == 1

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

5 个答案:

答案 0 :(得分: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();
    }
}

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

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

这是一种时髦的技巧,但可能不应该在生产代码中使用。这还不够清楚。 &amp;&amp; amp;和LHS中副作用的事实。 RHS要求操作员正常工作只是令人讨厌......虽然很有趣;)

编辑:我刚刚看到你想出了完全相同的东西,但任意长度。你的最终return语句是错误的 - 应该返回!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();
    }
}

编辑:现在对于功能性粉丝来说,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);
}

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

答案 1 :(得分: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;
 }

编辑:澄清后,从至少更改为完全

对于更通用和有效的解决方案(仅使用1个枚举器并检查序列是否实现ICollectionICollection<T>,在这种情况下不需要枚举),您可能需要查看我的回答here,可让您指定是否要查找ExactAtLeastAtMost个测试。

答案 2 :(得分:4)

seq.Skip(1).Any()会告诉您列表是否包含零个元素。

我认为您所做的编辑是关于检查长度为n的最有效方法。但是存在逻辑错误,长度小于长度的项目将返回true。看看我对第二个回复陈述做了什么。

    public static bool LengthEquals<T>(this IEnumerable<T> en, int length)
    {
        using (var er = en.GetEnumerator())
        {
            for (int i = 0; i < length; i++)
            {
                if (!er.MoveNext())
                    return false;
            }
            return !er.MoveNext();
        }
    }

答案 3 :(得分:1)

这个怎么样?

public static bool CountEquals<T>(this IEnumerable<T> source, int count) {
    return source.Take(count + 1).Count() == count;
}

Take()将确保我们永远不会将MoveNext的时间超过count+1次。

我想要注意的是,对于ICollection的任何实例,原始实现source.Count() == count应该更快,因为Count()已经过优化,只需查看Count成员

答案 4 :(得分:0)

我相信你所寻找的是.Single()。除了一个以外的任何东西都会抛出你可以捕获的InvalidOperationException。

http://msdn.microsoft.com/nb-no/library/bb155325.aspx