Count()
scans through all element, hence if (list.Count() == 1)
will not perform well if enumerable contains a lot of elements.
Single()
throws exception if there are not exactly one elements. Using try { list.Single(); } catch(InvalidOperationException e) {}
is clumsy and inefficient.
SingleOrDefault()
throws exception if there are more than one elements, hence if (list.SingleOrDefault() == null)
(assuming TSource
is of reference type) will not work for enumerables of size greater than one.
more direct:
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();
}
}
Note, however, that you can only guarantee that you can read a sequence once, so in those cases: by the simple fact of checking that there is a single item, you can no longer get the item. So you might prefer something that gives you the value if there is one:
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;
}