Come posso aggiungere un elemento a IEnumerable < T > collezione?
-
06-07-2019 - |
Domanda
La mia domanda come titolo sopra. Ad esempio,
IEnumerable<T> items = new T[]{new T("msg")};
items.ToList().Add(new T("msg2"));
ma dopo tutto contiene solo 1 oggetto.
Possiamo avere un metodo come items.Add (item)
?
come il List<T>
Soluzione
Non puoi, perché IEnumerable < T >
non rappresenta necessariamente una raccolta a cui è possibile aggiungere elementi. In realtà, non rappresenta necessariamente una collezione! Ad esempio:
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??
Quello che puoi fare, tuttavia, è creare un nuovo IEnumerable
oggetto (di tipo non specificato) che, quando elencato, fornirà tutti gli elementi di quello vecchio, più alcuni dei tuoi. Si utilizza Enumerable.Concat
per questo:
items = items.Concat(new[] { "foo" });
Questo non cambierà l'oggetto array (comunque non è possibile inserire elementi negli array). Ma creerà un nuovo oggetto che elencherà tutti gli elementi dell'array e quindi "Foo". Inoltre, quel nuovo oggetto terrà traccia delle modifiche nell'array (ovvero ogni volta che lo enumererai, vedrai i valori correnti degli elementi).
Altri suggerimenti
Il tipo IEnumerable < T >
non supporta tali operazioni. Lo scopo dell'interfaccia IEnumerable < T >
è consentire a un consumatore di visualizzare il contenuto di una collezione. Non modificare i valori.
Quando esegui operazioni come .ToList (). Add () stai creando un nuovo Elenco < T >
e aggiungendo un valore a tale elenco. Non ha alcun collegamento con l'elenco originale.
Quello che puoi fare è usare il metodo Aggiungi estensione per creare un nuovo IEnumerable < T >
con il valore aggiunto.
items = items.Add("msg2");
Anche in questo caso non modificherà l'oggetto IEnumerable < T >
originale. Questo può essere verificato tenendo un riferimento ad esso. Ad esempio
var items = new string[]{"foo"};
var temp = items;
items = items.Add("bar");
Dopo questo insieme di operazioni, la variabile temp farà ancora riferimento a un enumerabile con un singolo elemento "pippo". nell'insieme di valori mentre gli articoli faranno riferimento a un diverso enumerabile con valori "pippo" e "barra".
Modifica
Dimentico costantemente che Aggiungi non è un tipico metodo di estensione su IEnumerable < T >
perché è uno dei primi che alla fine definisco. Eccolo
public static IEnumerable<T> Add<T>(this IEnumerable<T> e, T value) {
foreach ( var cur in e) {
yield return cur;
}
yield return value;
}
Hai preso in considerazione l'utilizzo di ICollection < T >
o IList < T >
interfacce invece, esistono proprio per il motivo per cui si desidera avere un metodo Aggiungi
su un IEnumerable < T >
.
IEnumerable < T >
è usato per 'contrassegnare' un tipo come ... beh, enumerabile o solo una sequenza di elementi senza necessariamente fornire garanzie sul fatto che l'oggetto reale sottostante supporti l'aggiunta / rimozione di oggetti. Ricorda inoltre che queste interfacce implementano IEnumerable < T >
in modo da ottenere tutti i metodi di estensione che ottieni anche con IEnumerable < T >
.
Un paio di metodi di estensione breve e dolce su IEnumerable
e IEnumerable < T >
fallo per me:
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 (bene, tranne per le versioni non generiche). Peccato che questi metodi non siano nel BCL.
No, IEnumerable non supporta l'aggiunta di elementi.
La tua 'alternativa' è:
var List = new List(items);
List.Add(otherItem);
In .net Core esiste un metodo Enumerable.Append
che fa esattamente questo.
Il codice sorgente del metodo è disponibile su GitHub. .... L'implementazione (più sofisticata dei suggerimenti in altre risposte) merita una visita :).
Per aggiungere un secondo messaggio è necessario -
IEnumerable<T> items = new T[]{new T("msg")};
items = items.Concat(new[] {new T("msg2")})
Non solo non è possibile aggiungere elementi come si afferma, ma se si aggiunge un elemento a un Elenco < T >
(o praticamente a qualsiasi altra raccolta non di sola lettura) di cui si dispone enumeratore per, l'enumeratore è invalidato (genera InvalidOperationException
da quel momento in poi).
Se si stanno aggregando risultati da un tipo di query di dati, è possibile utilizzare il metodo di estensione Concat
:
Modifica: inizialmente ho usato l'estensione Union
, che non è proprio corretta. La mia applicazione lo utilizza ampiamente per assicurarsi che le query sovrapposte non duplicino i risultati.
IEnumerable<T> itemsA = ...;
IEnumerable<T> itemsB = ...;
IEnumerable<T> itemsC = ...;
return itemsA.Concat(itemsB).Concat(itemsC);
Sono appena venuto qui per dire che, oltre al metodo di estensione Enumerable.Concat
, sembra che ci sia un altro metodo chiamato Enumerable.Append
in .NET Core 1.1.1 . Quest'ultimo consente di concatenare un singolo elemento in una sequenza esistente. Quindi la risposta di Aamol può anche essere scritta come
IEnumerable<T> items = new T[]{new T("msg")};
items = items.Append(new T("msg2"));
Tuttavia, tieni presente che questa funzione non cambierà la sequenza di input, ma restituirà semplicemente un wrapper che mette insieme la sequenza data e l'elemento aggiunto.
Altri hanno già dato grandi spiegazioni sul perché non puoi (e non dovresti!) essere in grado di aggiungere elementi a un IEnumerable
. Aggiungerò che se stai cercando di continuare a scrivere codice su un'interfaccia che rappresenta una raccolta e desideri un metodo di aggiunta, dovresti codificare ICollection
o IList
. Come ulteriore aggiunta, queste interfacce implementano IEnumerable
.
puoi farlo.
//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;
Il modo più semplice per farlo è semplicemente
IEnumerable<T> items = new T[]{new T("msg")};
List<string> itemsList = new List<string>();
itemsList.AddRange(items.Select(y => y.ToString()));
itemsList.Add("msg2");
Quindi puoi restituire l'elenco come IEnumerable anche perché implementa l'interfaccia IEnumerable
Certo che puoi (lascio da parte la tua T-business):
public IEnumerable<string> tryAdd(IEnumerable<string> items)
{
List<string> list = items.ToList();
string obj = "";
list.Add(obj);
return list.Select(i => i);
}