As per my comment:
First, you should check to see if IEnumerable<T> source
actually is an IReadOnlyCollection<T>
or ICollection<T>
because that has a .Count
property you can use - which would be preferable to any iteration.
Assuming your IEnumerable<T>
does not have an O(1)
.Count
property, if you want to see if there's at least 1 element (i.e. "at least 2 or more") then use source.Take(2).Count() == 2
or source.Skip(1).Any()
.
Like so:
public static Boolean Many<T>( this IEnumerable<T> source )
{
if( source is null ) throw new ArgumentNullException(nameof(source));
if( source is ICollection<T> col ) return col.Count >= 2;
else if( source is IReadOnlyCollection<T> roCol ) return roCol.Count >= 2;
return source.Take(2).Count() == 2;
}
If you want to be more efficient about it, do manual iteration:
public static Boolean Many<T>( this IEnumerable<T> source )
{
if( source is null ) throw new ArgumentNullException(nameof(source));
if( source is ICollection<T> col ) return col.Count >= 2;
else if( source is IReadOnlyCollection<T> roCol ) return roCol.Count >= 2;
Int32 count = 0;
using( IEnumerator<T> iter = source.GetEnumerator() )
{
while( iter.MoveNext() && count < 2 )
{
count += 1;
}
}
return count == 2;
}
If you want to be even more efficient about it, allow consumers to supply non-boxed enumerators (e.g. List<T>.Enumerator
):
public static Boolean Many<TEnumerable,TEnumerator,TElement>( /*this*/ TEnumerable source, Func<TEnumerable,TEnumerator> getEnumerator )
where TEnumerable : IEnumerable<TElement>
where TEnumerator : IEnumerator<TElement>
{
if( source is null ) throw new ArgumentNullException(nameof(source));
if( getEnumerator is null ) throw new ArgumentNullException(nameof(getEnumerator));
//
if ( source is ICollection<TElement> col ) return col .Count >= 2;
else if( source is IReadOnlyCollection<TElement> roCol ) return roCol.Count >= 2;
Int32 count = 0;
using( TEnumerator iter = getEnumerator( source ) )
{
while( iter.MoveNext() && count < 2 )
{
count += 1;
}
}
return count == 2;
}
Used like so:
List<String> listOfStrings = new List<String>() { ... };
if( listOfStrings.Many</*TEnumerable:*/ List<String>, /*TEnumerator:*/ List<String>.Enumerator, /*TElement:*/ String >( l => l.GetEnumerator() ) )
{
}
- Yes, it's ugly... it's unfortunate, but C# still doesn't support this level of generic type-inference - nor does it support partial generic parameter application.
- The
list.GetEnumerator()
part is needed as it's the only way to pass a struct
-based enumerator into generic code without boxing (at least, not without reflection).