web-dev-qa-db-fra.com

Comment ajouter un élément à une collection IEnumerable <T>?

Ma question comme titre ci-dessus. Par exemple,

IEnumerable<T> items = new T[]{new T("msg")};
items.ToList().Add(new T("msg2"));

mais après tout, il ne contient qu'un seul article à l'intérieur.

Pouvons-nous avoir une méthode comme items.Add(item)?

comme le List<T>

372
ldsenow

Vous ne pouvez pas, car IEnumerable<T> ne représente pas nécessairement une collection à laquelle des éléments peuvent être ajoutés. En fait, cela ne représente pas nécessairement une collection du tout! Par exemple:

IEnumerable<string> ReadLines()
{
     string s;
     do
     {
          s = Console.ReadLine();
          yield return s;
     } while (s != "");
}

IEnumerable<string> lines = ReadLines();
lines.Add("foo") // so what is this supposed to do??

Ce que vous pouvez faire, cependant, est de créer un objet nouveaIEnumerable (de type non spécifié), qui, une fois énuméré, fournira tous les éléments de l'ancien, plus les vôtres. Vous utilisez Enumerable.Concat pour cela:

 items = items.Concat(new[] { "foo" });

Ceci ne changera pas l'objet tablea (de toute façon, vous ne pouvez pas insérer d'éléments dans les tableaux). Mais cela créera un nouvel objet qui listera tous les éléments du tableau, puis "Foo". En outre, ce nouvel objet suivra les modifications dans le tablea (c’est-à-dire que chaque fois que vous l’énumérerez, vous verrez les valeurs actuelles des éléments).

442
Pavel Minaev

Le type IEnumerable<T> ne prend pas en charge de telles opérations. Le but de l'interface IEnumerable<T> est de permettre à un consommateur de voir le contenu d'une collection. Ne pas modifier les valeurs.

Lorsque vous effectuez des opérations telles que .ToList (). Add (), vous créez un nouveau List<T> et ajoutez une valeur à cette liste. Il n'a aucun lien avec la liste d'origine.

Ce que vous pouvez faire est d'utiliser la méthode d'extension Add pour créer un nouveau IEnumerable<T> avec la valeur ajoutée.

items = items.Add("msg2");

Même dans ce cas, il ne modifiera pas l'objet d'origine IEnumerable<T>. Cela peut être vérifié en tenant une référence. Par exemple

var items = new string[]{"foo"};
var temp = items;
items = items.Add("bar");

Après cet ensemble d'opérations, la variable temp ne fera toujours référence qu'à un objet énumérable avec un seul élément "foo" dans l'ensemble de valeurs, tandis que les éléments feront référence à un objet énumérable différent avec les valeurs "foo" et "bar".

EDIT

J'oublie immédiatement que Add n'est pas une méthode d'extension typique sur IEnumerable<T> parce que c'est l'une des premières que je définisse. C'est ici

public static IEnumerable<T> Add<T>(this IEnumerable<T> e, T value) {
  foreach ( var cur in e) {
    yield return cur;
  }
  yield return value;
}
88
JaredPar

Avez-vous envisagé d'utiliser des interfaces ICollection<T> ou IList<T> , elles existent uniquement parce que vous souhaitez utiliser une méthode Add sur un _IEnumerable<T>_.

_IEnumerable<T>_ est utilisé pour 'marquer' un type comme étant ... bien, énumérable ou simplement une séquence d'éléments sans nécessairement garantir que l'objet réel sous-jacent prend en charge l'ajout/la suppression d'éléments. N'oubliez pas non plus que ces interfaces implémentent _IEnumerable<T>_ afin que vous obteniez également toutes les méthodes d'extensions que vous obtenez avec _IEnumerable<T>_.

52
Abhijeet Patel

Quelques méthodes d’extension simples et douces sur IEnumerable et IEnumerable<T> le font pour moi:

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);
}

Élégant (enfin, sauf pour les versions non génériques). Dommage que ces méthodes ne figurent pas dans la BCL.

31
Will

Non, l'IEnumerable ne prend pas en charge l'ajout d'éléments.

Vous 'alternative' c'est:

var List = new List(items);
List.Add(otherItem);
18
Paul van Brenk

Dans .net Core, il existe une méthode Enumerable.Append qui fait exactement cela.

Le code source de la méthode est disponible sur GitHub. .... L'implémentation (plus sophistiquée que les suggestions des autres réponses) mérite le détour :).

14
JanDotNet

Pour ajouter un deuxième message, vous devez -

IEnumerable<T> items = new T[]{new T("msg")};
items = items.Concat(new[] {new T("msg2")})
11
Aamol

Non seulement vous ne pouvez pas ajouter des éléments comme vous le dites, mais si vous ajoutez un élément à un List<T> (ou à peu près toute autre collection non-lecture seule) pour lequel vous avez un énumérateur existant, celui-ci est invalidé InvalidOperationException à partir de là).

Si vous agrégez les résultats d'un type de requête de données, vous pouvez utiliser la méthode d'extension Concat:

Edit: À l'origine, j'avais utilisé l'extension Union dans l'exemple, ce qui n'est pas vraiment correct. Mon application l'utilise beaucoup pour s'assurer que les requêtes qui se chevauchent ne dupliquent pas les résultats.

IEnumerable<T> itemsA = ...;
IEnumerable<T> itemsB = ...;
IEnumerable<T> itemsC = ...;
return itemsA.Concat(itemsB).Concat(itemsC);
8
Sam Harwell

Je viens juste de dire ici que, mis à part la méthode d'extension Enumerable.Concat, il semble exister une autre méthode appelée Enumerable.Append dans .NET Core 1.1.1. Ce dernier vous permet de concaténer un seul élément à une séquence existante. Donc, la réponse d'Aamol peut aussi être écrite comme suit:

IEnumerable<T> items = new T[]{new T("msg")};
items = items.Append(new T("msg2"));

Néanmoins, veuillez noter que cette fonction ne changera pas la séquence d'entrée, elle renverra simplement un wrapper qui assemble la séquence donnée et l'élément ajouté.

6
CXuesong

D'autres ont déjà fourni d'excellentes explications sur les raisons pour lesquelles vous ne pouvez pas (et ne devriez pas!) Pouvoir ajouter des éléments à un IEnumerable. J'ajouterai seulement que si vous souhaitez continuer à coder sur une interface qui représente une collection et souhaitez utiliser une méthode add, vous devez coder en ICollection ou IList . En bonanza supplémentaire, ces interfaces implémentent IEnumerable.

4
jason

tu peux le faire.

//Create IEnumerable    
IEnumerable<T> items = new T[]{new T("msg")};

//Convert to list.
List<T> list = items.ToList();

//Add new item to list.
list.add(new T("msg2"));

//Cast list to IEnumerable
items = (IEnumerable<T>)items;
1
Lrodriguez84

Le moyen le plus simple de le faire est tout simplement

IEnumerable<T> items = new T[]{new T("msg")};
List<string> itemsList = new List<string>();
itemsList.AddRange(items.Select(y => y.ToString()));
itemsList.Add("msg2");

Ensuite, vous pouvez retourner list en tant que IEnumerable également car il implémente l'interface IEnumerable

0
Juha Sinkkonen