检查IEnumerable是否只有一个元素的最佳方法是什么?

Jat*_*hvi 4 c# linq

Count()扫描所有元素,因此if (list.Count() == 1)如果enumerable包含很多元素,则效果不佳.

Single()如果没有一个元素,则抛出异常.使用try { list.Single(); } catch(InvalidOperationException e) {}是笨拙和低效的.

SingleOrDefault()如果有多个元素,则抛出异常,因此if (list.SingleOrDefault() == null)(假设TSource是引用类型)对大小大于1的可枚举不起作用.

nvo*_*igt 14

var exactlyOne = sequence.Take(2).Count() == 1;
Run Code Online (Sandbox Code Playgroud)

Take扩展方法不会抛出,如果有更少的元素,它只会只有那些获得回报.


Mar*_*ell 9

更直接:

public static bool HasSingle<T>(this IEnumerable<T> sequence) {
    if (sequence is ICollection<T> list) return list.Count == 1; // simple case
    using(var iter = sequence.GetEnumerator()) {
        return iter.MoveNext() && !iter.MoveNext();
    }
}
Run Code Online (Sandbox Code Playgroud)

但是请注意,你只能保证你能读一个序列一次,所以在这种情况下:通过检查,有一个简单的事实一个单一的项目,可以不再获得该项目.所以如果有的话,你可能更喜欢能给你价值的东西:

public static bool HasSingle<T>(this IEnumerable<T> sequence, out T value)
{
    if (sequence is IList<T> list)
    {
        if(list.Count == 1)
        {
            value = list[0];
            return true;
        }
    }
    else
    {
        using (var iter = sequence.GetEnumerator())
        {
            if (iter.MoveNext())
            {
                value = iter.Current;
                if (!iter.MoveNext()) return true;
            }
        }
    }

    value = default(T);
    return false;
}
Run Code Online (Sandbox Code Playgroud)


Ren*_*ogt 6

为了避免其他答案中的额外迭代,您可以实现自己的扩展:

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


Tim*_*ter 5

您可以使用!Skip(1).Any()

bool contains1 = items.Any() && !items.Skip(1).Any();
Run Code Online (Sandbox Code Playgroud)

如果类型是集合,则可以创建一个更有效的扩展:

public static bool ContainsCountItems<TSource>(this IEnumerable<TSource> source, int count)
{
    ICollection<TSource> collectionoft = source as ICollection<TSource>;
    if (collectionoft != null) return collectionoft.Count == count;
    ICollection collection = source as ICollection;
    if (collection != null) return collection.Count == count;
    int itemCount = 0;
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
        checked
        {
            while (e.MoveNext() && ++itemCount <= count)
            {
                if (itemCount == count)
                    return !e.MoveNext();
            }
        }
    }
    return false;
}
Run Code Online (Sandbox Code Playgroud)

用法:

var items = Enumerable.Range(0, 1);
bool contains1 = items.ContainsCountItems(1); // true;
items = Enumerable.Range(0, 2);
contains1 = items.ContainsCountItems(1); // false;
Run Code Online (Sandbox Code Playgroud)

您可以将此扩展名使用任何类型和任何数量,因此不仅1

var items = Enumerable.Range(0, 10);
bool contains10 = items.ContainsCountItems(10); // true;
Run Code Online (Sandbox Code Playgroud)


Dmi*_*nko 5

我建议与一起玩Any,我们必须检查一下

  1. list 至少有一项- Any
  2. list 没有第二项- !list.Skip(1).Any()

码:

  bool isSingle = list.Any() && !list.Skip(1).Any();
Run Code Online (Sandbox Code Playgroud)

但是这种方法有一个缺点:它扫描list 两次,以防万一IQueryable(查询执行两次,结果可能会有所不同,并且会产生额外开销)

  • 注意并不是所有的序列都是可重复的。如果`list`:是不可重复的,它将失败 (3认同)