Count()
会扫描所有元素,因此如果可枚举包含大量元素,则if (list.Count() == 1)
将无法正常运行。
Single()
会抛出异常。使用try { list.Single(); } catch(InvalidOperationException e) {}
是笨拙且低效的。
SingleOrDefault()
抛出异常,因此if (list.SingleOrDefault() == null)
(假设TSource
具有引用类型)将不适用于大小大于1的可枚举数。
答案 0 :(得分:14)
var exactlyOne = sequence.Take(2).Count() == 1;
如果元素较少,则Take
扩展方法不会抛出,只返回可用的元素。
答案 1 :(得分: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();
}
}
但请注意,您只能保证一次读取序列,因此在这些情况下:通过检查是单个的简单事实项目,你不能再获得该项目。所以如果有的话,你可能更喜欢能给你价值的东西:
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;
}
答案 2 :(得分:5)
您可以使用!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;
答案 3 :(得分:5)
我建议使用Any
,我们必须检查
list
至少有一项 - Any
list
没有第二项 - !list.Skip(1).Any()
代码:
bool isSingle = list.Any() && !list.Skip(1).Any();
但是这种方法有一个缺点:它会扫描list
两次,这可能是IQueryable
的问题(查询执行两次次)可能有不同的结果和额外的开销)
答案 4 :(得分:4)
为了避免在其他答案中进行额外的迭代,您可以实现自己的扩展:
public static bool HasExactlyOneElement<T>(this IEnumerable<T> source)
{
using (var enumerator = source.GetEnumerator())
return enumerator.MoveNext() && !enumerator.MoveNext();
}
答案 5 :(得分:2)
您调用原始枚举的每个Linq方法调用(.Any()
,.Skip()
,...)都会创建一个枚举器,根据您的要求,该枚举器也可能会受到相当大的性能影响。
所以你可以使用.Take(2).Count() == 1
。
另见There's a most performant way to check that a collection has exactly 1 element?。
答案 6 :(得分:0)
如果您因任何原因不想使用Linq,您也可以手动使用Enumerator:
/// <summary>
/// Checks that the IEnumerable<T> 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;
}
}