¿Cómo puedo agregar un elemento a una colección IEnumerable<T>?

Resuelto ldsenow asked hace 15 años • 17 respuestas

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>?

ldsenow avatar Jul 31 '09 08:07 ldsenow
Aceptado

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.Concatpara 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).

Pavel Minaev avatar Jul 31 '2009 02:07 Pavel Minaev

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;
}
JaredPar avatar Jul 31 '2009 01:07 JaredPar

¿Ha considerado utilizar interfaces ICollection<T>o IList<T>en su lugar? Existen por la misma razón que desea tener un Addmé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.

Abhijeet Patel avatar Jul 31 '2009 02:07 Abhijeet Patel

En .net Core, existe un método Enumerable.Appendque 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 :).

JanDotNet avatar Aug 12 '2017 19:08 JanDotNet

Un par de métodos de extensión breves y atractivos IEnumerabley 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.

 avatar Jan 10 '2013 18:01