En mi código necesito usar IEnumerable<>
varias veces para obtener el error Resharper de "Posible enumeración múltiple de IEnumerable
".
Código de muestra:
public List<object> Foo(IEnumerable<object> objects)
{
if (objects == null || !objects.Any())
throw new ArgumentException();
var firstObject = objects.First();
var list = DoSomeThing(firstObject);
var secondList = DoSomeThingElse(objects);
list.AddRange(secondList);
return list;
}
- Puedo cambiar el
objects
parámetro para que seaList
y luego evitar la posible enumeración múltiple, pero luego no obtengo el objeto más alto que puedo manejar. - Otra cosa que puedo hacer es convertir el
IEnumerable
alList
al principio del método:
public List<object> Foo(IEnumerable<object> objects)
{
var objectList = objects.ToList();
// ...
}
Pero esto es simplemente incómodo .
¿Qué haría usted en este escenario?
fuente
IReadOnlyCollection(T)
(nuevo con .net 4.5) como la mejor interfaz para transmitir la idea de que se trata deIEnumerable(T)
enumerar varias veces. Como dice esta respuesta, enIEnumerable(T)
sí misma es tan genérica que incluso puede referirse a contenido no reseteable que no se puede enumerar nuevamente sin efectos secundarios. PeroIReadOnlyCollection(T)
implica repetibilidad..ToList()
al comienzo del método, primero debe verificar si el parámetro no es nulo. Eso significa que obtendrá dos lugares donde arroja una excepción ArgumentException: si el parámetro es nulo y si no tiene ningún elemento.IReadOnlyCollection<T>
vsIEnumerable<T>
y luego publiqué una pregunta sobre el usoIReadOnlyCollection<T>
como parámetro, y en qué casos. Creo que la conclusión a la que finalmente llegué es la lógica a seguir. Que debe usarse donde se espera la enumeración, no necesariamente donde podría haber enumeración múltiple.Si sus datos siempre serán repetibles, tal vez no se preocupe por eso. Sin embargo, también puede desenrollarlo; esto es especialmente útil si los datos entrantes pueden ser grandes (por ejemplo, leer desde el disco / red):
Tenga en cuenta que cambié un poco la semántica de DoSomethingElse, pero esto es principalmente para mostrar el uso desenrollado. Podría volver a ajustar el iterador, por ejemplo. También podría convertirlo en un bloque iterador, lo que podría ser agradable; entonces no hay
list
, y usted recibiríayield return
los artículos a medida que los obtiene, en lugar de agregarlos a una lista para ser devueltos.fuente
Usar
IReadOnlyCollection<T>
oIReadOnlyList<T>
en la firma del método en lugar deIEnumerable<T>
, tiene la ventaja de hacer explícito que es posible que deba verificar el recuento antes de iterar, o iterar varias veces por alguna otra razón.Sin embargo, tienen un gran inconveniente que causará problemas si intentas refactorizar tu código para usar interfaces, por ejemplo, para que sea más comprobable y amigable para el proxy dinámico. El punto clave es que
IList<T>
no hereda deIReadOnlyList<T>
, y de manera similar para otras colecciones y sus respectivas interfaces de solo lectura. (En resumen, esto se debe a que .NET 4.5 quería mantener la compatibilidad ABI con versiones anteriores. Pero ni siquiera aprovecharon la oportunidad para cambiar eso en .NET core ) .Esto significa que si obtienes un
IList<T>
de alguna parte del programa y quieres pasarlo a otra parte que espera unIReadOnlyList<T>
, ¡no puedes! Sin embargo, puede pasar unIList<T>
como unIEnumerable<T>
.Al final,
IEnumerable<T>
es la única interfaz de solo lectura compatible con todas las colecciones .NET, incluidas todas las interfaces de colección. Cualquier otra alternativa volverá a morderte cuando te des cuenta de que te has bloqueado de algunas opciones de arquitectura. Por lo tanto, creo que es el tipo adecuado para usar en las firmas de funciones para expresar que solo desea una colección de solo lectura.(Tenga en cuenta que siempre puede escribir un
IReadOnlyList<T> ToReadOnly<T>(this IList<T> list)
método de extensión que convierta simples si el tipo subyacente admite ambas interfaces, pero debe agregarlo manualmente en todas partes al refactorizar, dondeIEnumerable<T>
siempre es compatible).Como siempre, esto no es absoluto, si está escribiendo un código pesado en la base de datos donde la enumeración múltiple accidental sería un desastre, es posible que prefiera una compensación diferente.
fuente
Si el objetivo es realmente evitar enumeraciones múltiples, entonces la respuesta de Marc Gravell es la que debe leerse, pero manteniendo la misma semántica, simplemente puede eliminar el redundante
Any
y lasFirst
llamadas e ir con:Tenga en cuenta que esto supone que usted
IEnumerable
no es genérico o al menos está limitado a ser un tipo de referencia.fuente
objects
todavía se pasa a DoSomethingElse, que devuelve una lista, por lo que aún existe la posibilidad de que sigas enumerando dos veces. De cualquier manera, si la colección fue una consulta LINQ to SQL, presionó la base de datos dos veces.First
arrojará una excepción para una lista vacía. No creo que sea posible decir nunca lanzar una excepción en la lista vacía o siempre lanzar una excepción en la lista vacía. Depende ...Por lo general, sobrecargo mi método con IEnumerable e IList en esta situación.
Me aseguro de explicar en los comentarios resumidos de los métodos que llamar a IEnumerable realizará un .ToList ().
El programador puede elegir .ToList () en un nivel superior si se concatenan varias operaciones y luego llamar a la sobrecarga de IList o dejar que mi sobrecarga IEnumerable se encargue de eso.
fuente
Si solo necesita verificar el primer elemento, puede echarle un vistazo sin iterar toda la colección:
fuente
En primer lugar, esa advertencia no siempre significa mucho. Por lo general, lo desactivé después de asegurarme de que no sea un cuello de botella de rendimiento. Simplemente significa que
IEnumerable
se evalúa dos veces, lo que generalmente no es un problema a menos que elevaluation
mismo tome mucho tiempo. Incluso si toma mucho tiempo, en este caso solo está usando un elemento la primera vez.En este escenario, también podría explotar los poderosos métodos de extensión de linq aún más.
Es posible evaluar solo
IEnumerable
una vez en este caso con algunos problemas, pero primero perfile y vea si realmente es un problema.fuente
IEnumerables
eso que da resultados diferentes cada vez que lo repite o que toma mucho tiempo repetirlo. Vea la respuesta de Marc Gravells: también declara en primer lugar "tal vez no se preocupe". La cuestión es que muchas veces ves esto y no tienes de qué preocuparte.