Продолжим обсуждать тему изменения коллекции внутри цикла foreach
. Следующий код
var list = new List<int> { 1, 2 };
foreach (var i in list)
{
if (i == 1)
list.Add(3);
Console.WriteLine(i);
}
выбросит InvalidOperationException
. А как думаете, что случится при выполнении цикла через List<T>.ForEach?
var list = new List<int> { 1, 2 };
list.ForEach(i =>
{
if (i == 1)
list.Add(3);
Console.WriteLine(i);
});
Правильный ответ: зависит.
Ранее (.NET 4.0) данный код замечательно отрабатывал и выводил 1 2 3
. Это было не очень хорошо. Поэтому в .NET 4.5 поведение поменялось, ForEach
начал бросать InvalidOperationException
для случая, если кто-то внутри цикла менял коллекцию:
public void ForEach(Action<T> action) {
if( action == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
}
Contract.EndContractBlock();
int version = _version;
for(int i = 0 ; i < _size; i++) {
if (version != _version && BinaryCompatibility.TargetsAtLeast_Desktop_V4_5) {
break;
}
action(_items[i]);
}
if (version != _version && BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
ThrowHelper.ThrowInvalidOperationException(
ExceptionResource.InvalidOperation_EnumFailedVersion);
}
Что касается Mono, то в нём никаких исключение не бросается (даже в последнем стабильном на текущий момент Mono 3.10). Когда я узнал, что в Mono 3.10 всё плохо, то очень расстроился. А потом пошёл и завёл баг-репорт. Вскоре баг был исправлен.
А в старых версий (например, 2.10) был баг, в результате которого исключение не происходило даже внутри обычного foreach
, если коллекцию менять через индексатор (ideone):
using System;
using System.Collections.Generic;
namespace test
{
public class test
{
public static void Main()
{
List x = new List();
x.Add(1);
x.Add(4);
x.Add(9);
foreach(int i in x){
x[2] = 3;
}
foreach(int i in x){
System.Console.WriteLine(i);
}
}
}
}
Actual Results (Mono 2.10):
1
4
3
Ссылки
- .NET Web Development and Tools Blog: All about : All about <httpRuntime targetFramework>
- .NET Framework Blog: .NET Framework 4.5 – Off to a great start
- ItDepends.NET: ListForEach
- MSDN: InvalidOperationException
- Mono Bug 699182: Modifications to a Collection via indexer during foreach should throw InvalidOperationException
- Mono Bug 24775: List.ForEach does not throw InvalidOperationException when collection was modified