我正在使用 .NET Framework 4.0。
神奇之處就在OrderBy()LINQ 方法中。這里有幾個例子供您參考:
var list = new List<int> { 1, 2, 3, 4, 5, 6};
foreach (var item in list)
{
if (item % 2 == 0)
list.Remove(item);
}
正如預期的那樣,此回圈會引發"System.InvalidOperationException: Collection was modified; enumeration operation may not execute."例外。
但是,如果我添加一個呼叫OrderBy():
foreach (var item in list.OrderBy(v => v))
{
if (item % 2 == 0)
list.Remove(item);
}
代碼執行得很好,從串列中洗掉了所有偶數。
起初我假設OrderBy()只是列舉了源串列并創建了它的排序副本。這很有意義并解釋了為什么回圈不會拋出例外:我沒有在修改時列舉相同的串列。但是在檔案(“備注”部分)中指出:
這個方法是通過延遲執行來實作的。立即回傳值是一個存盤執行操作所需的所有資訊的物件。在通過直接呼叫其 GetEnumerator 方法或在 Visual C# 中使用 foreach 或在 Visual Basic 中使用 For Each 來列舉物件之前,不會執行此方法表示的查詢。
那么這是檔案中的錯誤(可能是無意中復制粘貼了這個塊?)還是我錯過了什么?
PS有這個問題,但最受好評的答案假設OrderBy()只是列舉了串列。我很想知道這是否屬實(非常歡迎參考一些 .NET 源代碼)。也許副本確實沒有創建,但在我修改之前將源串列完全列舉?
uj5u.com熱心網友回復:
我認為這是因為當OrderBy開始執行時,它Buffer<T>通過呼叫在類中創建串列的副本ToArray,因此修改原始串列不會引發例外。這是對源代碼的參考
internal Buffer(IEnumerable<TElement> source)
{
if (source is IIListProvider<TElement> iterator)
{
TElement[] array = iterator.ToArray();
_items = array;
_count = array.Length;
}
else
{
_items = EnumerableHelpers.ToArray(source, out _count);
}
}
Buffer在GetEnumerator方法中初始化:
public IEnumerator<TElement> GetEnumerator()
{
Buffer<TElement> buffer = new Buffer<TElement>(_source);
if (buffer._count > 0)
{
int[] map = SortedMap(buffer);
for (int i = 0; i < buffer._count; i )
{
yield return buffer._items[map[i]];
}
}
}
uj5u.com熱心網友回復:
檔案中沒有錯誤 -list.OrderBy(v => v)被推遲(注意在此處執行),但是當您對其進行迭代時,foreach OrderBy將需要處理整個集合以確定所有元素的順序。
您可以通過引入副作用并在第一個元素處跳出回圈來查看它:
var list = new List<int> { 1, 2, 3, 4, 5, 6};
var xxx = list.OrderBy(v => {
Console.WriteLine(v);
return v;
});
// next will print "before" and then all collection elements
Console.WriteLine("before");
foreach (var item in xxx)
{
break;
}
并且在執行期間內部(.NET Framework 版本)OrderBy將創建傳入可列舉的副本:
public IEnumerator<TElement> GetEnumerator() {
Buffer<TElement> buffer = new Buffer<TElement>(source); // will copy elements here
if (buffer.count > 0) {
EnumerableSorter<TElement> sorter = GetEnumerableSorter(null);
int[] map = sorter.Sort(buffer.items, buffer.count);
sorter = null;
for (int i = 0; i < buffer.count; i ) yield return buffer.items[map[i]];
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/369805.html
