簡體   English   中英

如何檢查 IEnumerable 是否只有一個元素?

[英]How do I check if IEnumerable has a single element?

Count()掃描所有元素,因此如果 enumerable 包含很多元素, if (list.Count() == 1)將不會表現良好。

如果不完全是一個元素, Single()會拋出異常。 使用try { list.Single(); } catch(InvalidOperationException e) {} try { list.Single(); } catch(InvalidOperationException e) {}既笨拙又低效。

如果有多個元素, SingleOrDefault()會拋出異常,因此if (list.SingleOrDefault() == null) (假設TSource是引用類型)將不適用於大小大於 1 的枚舉。

var exactlyOne = sequence.Take(2).Count() == 1;

如果元素較少, Take擴展方法不會拋出,它只會返回可用的元素。

更直接:

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

為了避免其他答案中的額外迭代,您可以實現自己的擴展:

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

您可以使用!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;

您可以將此擴展用於任何類型和任何數量,因此不僅是 1

var items = Enumerable.Range(0, 10);
bool contains10 = items.ContainsCountItems(10); // true;

我建議玩Any ,我們必須檢查一下

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

代碼:

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

然而,這種方法有缺點:它掃描list兩次,這在IQueryable情況下可能是一個問題(查詢執行兩次,結果可能不同,額外開銷)

您在原始枚舉上調用的每個 Linq 方法調用( .Skip() .Any().Skip() 、...)都會創建一個枚舉器,根據您的要求,這也可能對性能造成相當大的影響。

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

另請參閱有一種最高效的方法來檢查集合是否恰好有 1 個元素? .

如果您出於某種原因不想使用 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;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM