Acabo de leer un artículo interesante llamado Cómo ser demasiado lindo con retorno de rendimiento de C #
Me hizo preguntarme cuál es la mejor manera de detectar si un IEnumerable es una colección real enumerable, o si es una máquina de estado generada con la palabra clave de rendimiento.
Por ejemplo, puede modificar DoubleXValue (del artículo) a algo como:
private void DoubleXValue(IEnumerable<Point> points)
{
if(points is List<Point>)
foreach (var point in points)
point.X *= 2;
else throw YouCantDoThatException();
}
Pregunta 1) ¿Hay una mejor manera de hacer esto?
Pregunta 2) ¿Es esto algo de lo que debería preocuparme al crear una API?
c#
api-design
CondiciónRacer
fuente
fuente
ICollection<T>
sería una mejor opción ya que no todas las colecciones lo sonList<T>
. Por ejemplo, las matrices sePoint[]
implementanIList<T>
pero no lo sonList<T>
.IEnumerable
es inmutable en el sentido de que no puede modificar una colección (agregar / eliminar) mientras la enumera, sin embargo, si los objetos en la lista son mutables, es perfectamente razonable modificarlos mientras enumera la lista.ToList
? msdn.microsoft.com/en-us/library/bb342261.aspx No detecta si se generó por rendimiento, sino que lo hace irrelevante.Respuestas:
Su pregunta, según tengo entendido, parece estar basada en una premisa incorrecta. Déjame ver si puedo reconstruir el razonamiento:
El problema es que la segunda premisa es falsa. Incluso si pudiera detectar si un IEnumerable dado fue el resultado de una transformación de bloque iterador (y sí, hay formas de hacerlo) no ayudaría porque la suposición es incorrecta. Vamos a ilustrar por qué.
Muy bien, tenemos cuatro métodos. S1 y S2 son secuencias generadas automáticamente; S3 y S4 son secuencias generadas manualmente. Ahora supongamos que tenemos:
El resultado para S1 y S4 será 0; cada vez que enumera la secuencia, obtiene una nueva referencia a una M creada. El resultado para S2 y S3 será 100; cada vez que enumera la secuencia, obtiene la misma referencia a M que obtuvo la última vez. Si el código de secuencia se genera automáticamente o no es ortogonal a la pregunta de si los objetos enumerados tienen identidad referencial o no. Esas dos propiedades, la generación automática y la identidad referencial, en realidad no tienen nada que ver entre sí. El artículo que vinculaste los combina de alguna manera.
A menos que un proveedor de secuencias esté documentado como siempre ofreciendo objetos que tienen identidad referencial , no es prudente suponer que lo hace.
fuente
Creo que el concepto clave es que debes tratar cualquier
IEnumerable<T>
colección como inmutable : no debes modificar los objetos a partir de ella, debes crear una nueva colección con nuevos objetos:(O escriba lo mismo de manera más sucinta usando LINQ
Select()
).Si realmente desea modificar los elementos de la colección, no use
IEnumerable<T>
, useIList<T>
(o posiblementeIReadOnlyList<T>
si no desea modificar la colección en sí, solo los elementos que contiene y está en .Net 4.5):De esta manera, si alguien intenta usar su método
IEnumerable<T>
, fallará en el momento de la compilación.fuente
IEnumerable
vez. La necesidad de hacerlo más de una vez puede obviarse llamandoToList()
e iterando sobre la lista, aunque en el caso específico mostrado por el OP, prefiero que la solución de svick de tener DoubleXValue también devuelva un IEnumerable. Por supuesto, en código real probablemente estaría usandoLINQ
para generar este tipo deIEnumerable
. Sin embargo, la elección de svickyield
tiene sentido en el contexto de la pregunta del OP.Select()
. Y con respecto a múltiples iteraciones deIEnumerable
: ReSharper es útil con eso, porque puede advertirte cuando lo haces.Personalmente, creo que el problema resaltado en ese artículo proviene del uso excesivo de la
IEnumerable
interfaz por parte de muchos desarrolladores de C # en estos días. Parece que se ha convertido en el tipo predeterminado cuando se requiere una colección de objetos, pero hay mucha información queIEnumerable
simplemente no te dice ... crucialmente: si se ha reificado o no *.Si encuentra que necesita detectar si un
IEnumerable
es realmente unIEnumerable
o algo más, la abstracción se ha filtrado y probablemente se encuentre en una situación en la que su tipo de parámetro es demasiado flojo. Si necesita la información adicional queIEnumerable
solo no proporciona, explique ese requisito eligiendo una de las otras interfaces comoICollection
oIList
.Editar para los downvoters Por favor regrese y lea la pregunta cuidadosamente. El OP está pidiendo una manera de garantizar que las
IEnumerable
instancias se hayan materializado antes de pasar a su método API. Mi respuesta aborda esa pregunta. Hay un problema más amplio con respecto a la forma correcta de usarIEnumerable
, y ciertamente las expectativas del código que el OP pegó se basan en un malentendido de ese problema más amplio, pero el OP no preguntó cómo usar correctamenteIEnumerable
o cómo trabajar con tipos inmutables . No es justo rechazarme por no responder una pregunta que no se hizo.* Yo uso el término
reify
, otros usanstabilize
omaterialize
. No creo que la comunidad haya adoptado una convención común todavía.fuente
ICollection
yIList
es que son mutables (o al menos se ven de esa manera).IReadOnlyCollection
yIReadOnlyList
de .Net 4.5 resuelven algunos de esos problemas.