¿Cómo puedo agregar un elemento a una colección IEnumerable<T>?
Mi pregunta como título arriba. Por ejemplo
IEnumerable<T> items = new T[]{new T("msg")};
items.ToList().Add(new T("msg2"));
pero después de todo sólo tiene 1 artículo dentro. ¿ Podemos tener un método como items.Add(item)
el List<T>
?
No es posible, porque IEnumerable<T>
no representa necesariamente una colección a la que se puedan agregar elementos. De hecho, ¡no representa necesariamente una colección en absoluto! Por ejemplo:
IEnumerable<string> ReadLines()
{
string s;
do
{
s = Console.ReadLine();
yield return s;
} while (!string.IsNullOrEmpty(s));
}
IEnumerable<string> lines = ReadLines();
lines.Add("foo") // so what is this supposed to do??
Sin embargo, lo que puede hacer es crear un nuevo IEnumerable
objeto (de tipo no especificado) que, cuando se enumere, proporcionará todos los elementos del antiguo, además de algunos propios. Usas Enumerable.Concat
para eso:
items = items.Concat(new[] { "foo" });
Esto no cambiará el objeto de la matriz (de todos modos, no puede insertar elementos en las matrices). Pero creará un nuevo objeto que enumerará todos los elementos de la matriz y luego "Foo". Además, ese nuevo objeto realizará un seguimiento de los cambios en la matriz (es decir, cada vez que lo enumere, verá los valores actuales de los elementos).
El tipo IEnumerable<T>
no admite este tipo de operaciones. El propósito de la IEnumerable<T>
interfaz es permitir al consumidor ver el contenido de una colección. No modificar los valores.
Cuando realizas operaciones como .ToList().Add() estás creando una nueva List<T>
y agregando un valor a esa lista. No tiene conexión con la lista original.
Lo que puedes hacer es usar el método Agregar extensión para crear una nueva IEnumerable<T>
con el valor agregado.
items = items.Add("msg2");
Incluso en este caso no modificará el IEnumerable<T>
objeto original. Esto se puede verificar manteniendo una referencia al mismo. Por ejemplo
var items = new string[]{"foo"};
var temp = items;
items = items.Add("bar");
Después de este conjunto de operaciones, la variable temp solo hará referencia a un enumerable con un único elemento "foo" en el conjunto de valores, mientras que los elementos harán referencia a un enumerable diferente con los valores "foo" y "bar".
EDITAR
Constantemente olvido que Agregar no es un método de extensión típico IEnumerable<T>
porque es uno de los primeros que termino definiendo. Aquí lo tienes
public static IEnumerable<T> Add<T>(this IEnumerable<T> e, T value) {
foreach ( var cur in e) {
yield return cur;
}
yield return value;
}
¿Ha considerado utilizar interfaces ICollection<T>
o IList<T>
en su lugar? Existen por la misma razón que desea tener un Add
método en un archivo IEnumerable<T>
.
IEnumerable<T>
se utiliza para 'marcar' un tipo como... bueno, enumerable o simplemente una secuencia de elementos sin necesariamente ofrecer ninguna garantía de si el objeto subyacente real admite la adición/eliminación de elementos. Recuerde también que estas interfaces se implementan IEnumerable<T>
para que usted IEnumerable<T>
también obtenga todos los métodos de extensión que obtiene.
En .net Core, existe un método Enumerable.Append
que hace exactamente eso.
El código fuente del método está disponible en GitHub. .... Vale la pena echarle un vistazo a la implementación (más sofisticada que las sugerencias en otras respuestas :).
Un par de métodos de extensión breves y atractivos IEnumerable
y IEnumerable<T>
hazlo por mí:
public static IEnumerable Append(this IEnumerable first, params object[] second)
{
return first.OfType<object>().Concat(second);
}
public static IEnumerable<T> Append<T>(this IEnumerable<T> first, params T[] second)
{
return first.Concat(second);
}
public static IEnumerable Prepend(this IEnumerable first, params object[] second)
{
return second.Concat(first.OfType<object>());
}
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> first, params T[] second)
{
return second.Concat(first);
}
Elegante (bueno, excepto las versiones no genéricas). Lástima que estos métodos no estén en la BCL.