Esto debe hacerlo:
public static bool ContainsExactlyOneItem<T>(this IEnumerable<T> source)
{
using (IEnumerator<T> iterator = source.GetEnumerator())
{
// Check we've got at least one item
if (!iterator.MoveNext())
{
return false;
}
// Check we've got no more
return !iterator.MoveNext();
}
}
Usted podría eludir esto más, pero no sugerimos que lo haga:
public static bool ContainsExactlyOneItem<T>(this IEnumerable<T> source)
{
using (IEnumerator<T> iterator = source.GetEnumerator())
{
return iterator.MoveNext() && !iterator.MoveNext();
}
}
Es el tipo de truco que es funky, pero probablemente no debería usarse en el código de producción. Simplemente no es lo suficientemente claro. El hecho de que se requiere el efecto secundario en el LHS del operador & & para el RHS para trabajar adecuadamente es sólo desagradable ... mientras que un montón de diversión;)
EDIT: acabo de ver que haya venido con exactamente lo mismo, pero para una longitud arbitraria. Sin embargo, su declaración final de devolución está equivocada, debería ser la devolución !en.MoveNext()
. He aquí un método completo con un nombre más bonito (OMI), chequeo de argumentos y la optimización de ICollection
/ICollection<T>
:
public static bool CountEquals<T>(this IEnumerable<T> source, int count)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("count",
"count must not be negative");
}
// We don't rely on the optimizations in LINQ to Objects here, as
// they have changed between versions.
ICollection<T> genericCollection = source as ICollection<T>;
if (genericCollection != null)
{
return genericCollection.Count == count;
}
ICollection nonGenericCollection = source as ICollection;
if (nonGenericCollection != null)
{
return nonGenericCollection.Count == count;
}
// Okay, we're finally ready to do the actual work...
using (IEnumerator<T> iterator = source.GetEnumerator())
{
for (int i = 0; i < count; i++)
{
if (!iterator.MoveNext())
{
return false;
}
}
// Check we've got no more
return !iterator.MoveNext();
}
}
EDIT: Y ahora para los fanáticos funcionales, una forma recursiva de CountEquals
(no utilizan por favor este , es sólo aquí por diversión):
public static bool CountEquals<T>(this IEnumerable<T> source, int count)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("count",
"count must not be negative");
}
using (IEnumerator<T> iterator = source.GetEnumerator())
{
return IteratorCountEquals(iterator, count);
}
}
private static bool IteratorCountEquals<T>(IEnumerator<T> iterator, int count)
{
return count == 0 ? !iterator.MoveNext()
: iterator.MoveNext() && IteratorCountEquals(iterator, count - 1);
}
EDIT: Tenga en cuenta que para algo como LINQ a SQL, debe utilizar el enfoque simple Count()
- ya que va a permitir que se realiza en la base de datos en lugar de después de ir a buscar resultados actuales.
Creo que el error de lógica que hice en mi código puede haber arrojado personas con respecto a * al menos * y * exactamente *. Creo que hemos llegado a la mejor solución ahora. – guhou
Count() solo repetirá la lista completa si determina que el IEnumerable pasado no se transfiere a ICollection . Por lo tanto, si pasa una instancia de List o una matriz, no se repetirá. –
Creo que esto es una optimización prematura. –