代码之家  ›  专栏  ›  技术社区  ›  Jatin Sanghvi

如何检查IEnumerable是否有单个元素?

  •  9
  • Jatin Sanghvi  · 技术社区  · 7 年前

    Count() 扫描所有元素,因此 if (list.Count() == 1) 如果enumerable包含大量元素,则无法很好地执行。

    Single() 如果不存在一个元素,则引发异常。使用 try { list.Single(); } catch(InvalidOperationException e) {} 是笨拙和低效的。

    SingleOrDefault() 如果有多个元素,则引发异常,因此 if (list.SingleOrDefault() == null) (假设 TSource 是引用类型)将不适用于大小大于1的枚举。

    7 回复  |  直到 4 年前
        1
  •  18
  •   nvoigt    7 年前
    var exactlyOne = sequence.Take(2).Count() == 1;
    

    这个 Take 如果元素较少,扩展方法将不会抛出,它将只返回可用的元素。

        2
  •  14
  •   Marc Gravell    7 年前

    更直接:

    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();
        }
    }
    

    然而,请注意,您只能保证可以读取一次序列,因此 在这些情况下 :通过检查 如果是单个项目,则无法再获取该项目。因此,如果有价值的东西,您可能更喜欢它:

    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;
    }
    
        3
  •  7
  •   René Vogt    7 年前

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

    public static bool HasExactlyOneElement<T>(this IEnumerable<T> source)
    {
        using (var enumerator = source.GetEnumerator())
            return enumerator.MoveNext() && !enumerator.MoveNext();
    }
    
        4
  •  4
  •   Tim Schmelter    7 年前

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

    bool contains1 = items.Any() && !items.Skip(1).Any();
    

    如果类型为集合,则可以创建更高效的扩展:

    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;
    }
    

    用法:

    var items = Enumerable.Range(0, 1);
    bool contains1 = items.ContainsCountItems(1); // true;
    items = Enumerable.Range(0, 2);
    contains1 = items.ContainsCountItems(1); // false;
    

    您可以将此扩展用于任何类型和任何计数,因此

    var items = Enumerable.Range(0, 10);
    bool contains10 = items.ContainsCountItems(10); // true;
    
        5
  •  3
  •   Dmitry Bychenko    7 年前

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

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

    代码:

      bool isSingle = list.Any() && !list.Skip(1).Any();
    

    然而,这种方法有一个缺点:它扫描 列表 两次 在以下情况下,这可能是一个问题 IQueryable (执行查询 具有潜在不同结果和额外开销的时间)

        6
  •  2
  •   CodeCaster    7 年前

    每个Linq方法调用( .Any() , .Skip() , ...) 您调用原始枚举创建了一个枚举器,根据您的需求,这可能会对性能造成相当大的影响。

    所以你可以 .Take(2).Count() == 1 .

    另请参见 There's a most performant way to check that a collection has exactly 1 element? .

        7
  •  1
  •   Wai Ha Lee Jun ZHOU    7 年前

    如果出于任何原因不想使用Linq,也可以手动使用 Enumerator :

    /// <summary>
    /// Checks that the IEnumerable&lt;T&gt; has exactly one item
    /// </summary>
    public static bool HasSingleElement<T>(IEnumerable<T> value)
    {
        using ( var enumerator = value.GetEnumerator() )
        {
            // Try to get first element - return false if that doesn't exist
            if ( !enumerator.MoveNext() )
                return false;
    
            // Try to get second element - return false if it does exist
            if ( enumerator.MoveNext() )
                return false;
    
            // exactly one element exists
            return true;
        }
    }