关于c#:是否有LINQ扩展或(一组合理/有效的LINQ扩展)确定集合是否至少具有’x’元素?

Is there a LINQ extension or (a sensible/efficient set of LINQ entensions) that determine whether a collection has at least 'x' elements?

我有代码需要知道集合不应该是空的或只包含一个项。

一般来说,我希望表单的扩展名为:

bool collectionHasAtLeast2Items = collection.AtLeast(2);

我可以很容易地编写扩展,枚举集合并增加索引器,直到达到所请求的大小,或者元素用完,但是linq框架中是否已经有了可以这样做的东西?我的想法(按我的想法顺序)是:

bool collectionHasAtLeast2Items = collection.Take(2).Count() == 2;

bool collectionHasAtLeast2Items = collection.Take(2).ToList().Count == 2;

虽然(在文档中)没有定义获取的元素多于集合所包含的元素的行为是可枚举的,但take方法似乎可以实现预期的效果。

这不是最有效的解决方案,要么枚举一次以获取元素,然后再次枚举以对其进行计数(这是不必要的),要么枚举一次以获取元素,然后构造一个列表以获取非枚举器-y的Count属性,因为我实际上不需要该列表。

这并不是很漂亮,因为我总是要做出两个断言,第一个是"x",然后检查我是否真的收到了"x",这取决于未记录的行为。

或者我可以使用:

bool collectionHasAtLeast2Items = collection.ElementAtOrDefault(2) != null;

然而,这在语义上并不清楚。也许最好是用一个方法名来包装它,这意味着我想要什么。我假设这将是有效的,我没有考虑到代码。

其他一些想法正在使用Last(),但我明确不想枚举整个集合。

或者可能是Skip(2).Any(),同样不是完全明显的语义,但比ElementAtOrDefault(2) != null更好,尽管我认为它们会产生相同的结果?

有什么想法吗?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static bool AtLeast<T>(this IEnumerable<T> source, int count)
{
    // Optimization for ICollection<T>
    var genericCollection = source as ICollection<T>;
    if (genericCollection != null)
        return genericCollection.Count >= count;

    // Optimization for ICollection
    var collection = source as ICollection;
    if (collection != null)
        return collection.Count >= count;

    // General case
    using (var en = source.GetEnumerator())
    {
        int n = 0;
        while (n < count && en.MoveNext()) n++;
        return n == count;
    }
}


如果序列实现了ICollection,那么可以使用Count() >= 2

在场景后面,Enumerable.Count()扩展方法检查循环下的序列是否实现ICollection。如果确实如此,则返回Count属性,因此目标性能应为o(1)。

因此,((IEnumerable)((ICollection)sequence)).Count() >= x也应该有O(1)。


你可以使用Count,但如果性能有问题,你最好使用Take

1
bool atLeastX = collection.Take(x).Count() == x;

由于Take(我相信)使用了延迟执行,它只会执行一次收集。

Abatishchev提到,CountICollection是O(1),所以你可以这样做,并得到最好的两个世界。

1
2
3
4
5
6
7
8
9
10
11
12
13
IEnumerable<int> col;
// set col
int x;
// set x
bool atLeastX;
if (col is ICollection<int>)
{
    atLeastX = col.Count() >= x;
}
else
{
    atLeastX = col.Take(x).Count() == x;
}

你也可以使用Skip/Any,事实上,我打赌它会比Take/Count更快。