我的問題如上所述。例如,如何將項添加到IEnumerable <T>集合中?
IEnumerable<T> items = new T[]{new T("msg")};
items.ToList().Add(new T("msg2"));
但畢竟它只有1項內。
我們可以有一個像items.Add(item)
這樣的方法嗎?
像List<T>
我的問題如上所述。例如,如何將項添加到IEnumerable <T>集合中?
IEnumerable<T> items = new T[]{new T("msg")};
items.ToList().Add(new T("msg2"));
但畢竟它只有1項內。
我們可以有一個像items.Add(item)
這樣的方法嗎?
像List<T>
您不能,因爲IEnumerable<T>
不一定代表可以添加項目的集合。事實上,它並不一定代表一個集合!例如:
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??
你能做什麼,但是,是創建(未指定類型)的新IEnumerable
對象,其中,列舉時,將提供舊的所有項目,加上一些自己的。您可以使用Enumerable.Concat
爲:
items = items.Concat(new[] { "foo" });
這不會改變數組對象(你不能將項目插入到陣列,反正)。但它會創建一個新的對象,列出數組中的所有項目,然後是「Foo」。此外,新對象會跟蹤數組中的更改(即,每當您枚舉它時,都會看到項目的當前值)。
創建一個數組來添加單個值的開銷有點高。定義單個值Concat的擴展方法可以重複使用一點。 – JaredPar 2009-07-31 02:08:45
這取決於 - 它可能是值得的,但我很想把它稱爲過早優化,除非重複地在循環中調用'Concat';如果是的話,無論如何你都會遇到更大的問題,因爲每次你使用'Concat'時,你會得到一個枚舉器層次 - 所以到最後,單次調用MoveNext會導致一系列長度相等的調用到迭代次數。 – 2009-07-31 02:13:20
@Pavel,heh。通常情況下,創建陣列的內存開銷並不會讓我感到困擾。這是額外的打字,我覺得煩人:)。 – JaredPar 2009-07-31 02:15:34
沒有了IEnumerable好好嘗試一下支持將項目添加到它。
你有一個'替代'。
var List = new List(items);
List.Add(otherItem);
您的建議不是唯一的選擇。例如,ICollection和IList在這裏都是合理的選擇。 – jason 2009-07-31 02:07:53
但是你不能實例化。 – 2009-07-31 02:42:14
當然,你也不能實例化 - 它們是接口。 – 2010-10-15 15:16:23
IEnumerable<T>
類型不支持這種操作。 IEnumerable<T>
接口的目的是讓消費者查看集合的內容。不要修改這些值。
當您執行像.ToList()。Add()這樣的操作時,您將創建一個新的List<T>
並向該列表中添加一個值。它與原始列表沒有關係。
你可以做的是使用Add擴展方法創建一個新的IEnumerable<T>
與增值。
items = items.Add("msg2");
即使在這種情況下,它不會修改原始IEnumerable<T>
對象。這可以通過參考它來驗證。例如
var items = new string[]{"foo"};
var temp = items;
items = items.Add("bar");
這組操作後,將變量temp將仍然只能引用可枚舉與該組值中的單個元素「foo」的,而項目將參考不同的枚舉與值「foo」的和「bar 」。
編輯
我contstantly忘了加不上IEnumerable<T>
一個典型的擴展方法,因爲它是我最終確定了第一批之一。這是
public static IEnumerable<T> Add<T>(this IEnumerable<T> e, T value) {
foreach (var cur in e) {
yield return cur;
}
yield return value;
}
這就是所謂的'Concat' :) – 2009-07-31 02:01:23
@Pavel,謝謝你指出。即使Concat也不行,因爲它需要另一個IEnumerable
雖然我不會稱之爲「添加」,因爲實際上任何其他.NET類型的「添加」(不僅僅是集合)都會就地改變集合。也許'用'?或者它甚至可能只是'Concat'的另一個重載。 – 2009-07-31 02:15:07
不僅喜歡你的國家,你能不能添加項目,但如果添加一個項目到List<T>
(或幾乎任何其他非只讀集合),您必須對現有的枚舉,該枚舉器失效(從此之後拋出InvalidOperationException
)。
如果您彙集來自某種類型的數據查詢的結果,你可以使用Concat
擴展方法:
編輯:我原來使用的Union
擴展的例子,這是不是真的正確。我的應用程序廣泛使用它來確保重疊查詢不會重複結果。
IEnumerable<T> itemsA = ...;
IEnumerable<T> itemsB = ...;
IEnumerable<T> itemsC = ...;
return itemsA.Concat(itemsB).Concat(itemsC);
其他人已經給出了很好的解釋,爲什麼你不能(也不應該)能夠將項目添加到IEnumerable
。我只會補充一點,如果您希望繼續編碼到代表集合的接口並希望添加方法,則您應該編碼爲ICollection
或IList
。作爲一個附加的富礦,這些接口實現IEnumerable
。
你有沒有使用ICollection<T>或IList<T>接口,而不是考慮他們存在,你想有一個「添加」方法對一個IEnumerable <牛逼>的真正原因。 IEnumerable <T>用於將某個類型標記爲......良好......可枚舉或只是一系列項目,而不必確保真實底層對象是否支持添加/刪除項目。還請記住,這些接口實現IEnumerable <T>,因此您可以使用IEnumerable <T>獲得所有擴展方法。
幾篇短,甜美的擴展上IEnumerable
和IEnumerable<T>
方法做到這一點對我來說:
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);
}
優雅的(當然,除了非通用版本)。太糟糕了,這些方法不在BCL中。
第一個Prepend方法給我一個錯誤。 「'object []'不包含'Concat'的定義和最佳擴展方法重載'System.Linq.Enumerable.Concat
@TimNewton你做錯了。誰知道爲什麼,因爲沒有人能從這段錯誤代碼中看出來。 Protip:總是捕獲異常並在其上執行'ToString()',因爲捕獲了更多的錯誤細節。我猜你沒有在文件頂部包含System.Linq;但是誰知道?嘗試自行研究,創建一個最小原型,如果你不能,那麼會出現同樣的錯誤,然後在問題中尋求幫助。 – Will 2013-07-23 14:45:32
我剛剛複製並粘貼上面的代碼。除了提到我提到的錯誤的Prepend之外,它們都可以正常工作。它不是一個運行時異常,它是一個編譯時異常。 – 2013-07-23 16:32:01
你可以這樣做。
//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;
這個問題在5年前以更完整的方式回答。將接受的答案看作問題的良好答案的例子。 – pquest 2014-11-28 18:38:40
Easyest方法就是
IEnumerable<T> items = new T[]{new T("msg")};
List<string> itemsList = new List<string>();
itemsList.AddRange(items.Select(y => y.ToString()));
itemsList.Add("msg2");
然後你就可以回到列表爲IEnumerable還因爲它實現IEnumerable接口
要添加第二個消息,則需要 -
IEnumerable<T> items = new T[]{new T("msg")};
items = items.Concat(new[] {new T("msg2")})
但您還需要將Concat方法的結果分配給項目對象。 – 2017-02-09 15:33:30
是的,托馬斯你是對的。我修改了最後一個表達式來將連接值分配給項目。 – Aamol 2017-02-17 03:06:36
當然,你可以(我將離開你的T-business):
public IEnumerable<string> tryAdd(IEnumerable<string> items)
{
List<string> list = items.ToList();
string obj = "";
list.Add(obj);
return list.Select(i => i);
}
我只是來這裏說,除了Enumerable.Concat
擴展方法,似乎是在.NET核心1.1.1命名Enumerable.Append
另一種方法。後者允許您將單個項目連接到現有序列。所以Aamol的回答也可以寫成
IEnumerable<T> items = new T[]{new T("msg")};
items = items.Append(new T("msg2"));
不過,請注意,此功能不會改變輸入序列,它只是返回一個包裝,把給定的順序和附加項目一起。
在.net核心中,有一個方法Enumerable.Append
正是如此。
該方法的源代碼是available on GitHub. ....實現(比其他答案中的建議更復雜)值得一看:)。
'IEnumerable'僅用於查詢集合。它是LINQ框架的支柱。它總是一些其他集合的抽象,例如'Collection ','List '或'Array'。該接口只提供了一個'GetEnumerator'方法,該方法返回一個'IEnumerator '的實例,該實例可以一次擴展一個項目。 LINQ擴展方法受此界面限制。 'IEnumerable '被設計爲只讀,因爲它可能代表多個集合的部分集合。 –
Jordan
2016-01-27 16:04:56
對於這個例子,只聲明'IList'而不是'IEnumerable ':'IList items = new T [] {new T(「msg」)}; items.Add(new T(「msg2」));' –
NightOwl888
2017-05-07 15:56:57