为什么这段代码会抛出'Collection was modified',但是当我在它之前迭代一些东西时,它却没有?

Lyi*_*Sky 102 c# collections

var ints = new List< int >( new[ ] {
    1,
    2,
    3,
    4,
    5
} );
var first = true;
foreach( var v in ints ) {
    if ( first ) {
        for ( long i = 0 ; i < int.MaxValue ; ++i ) { //<-- The thing I iterate
            ints.Add( 1 );
            ints.RemoveAt( ints.Count - 1 );
        }
        ints.Add( 6 );
        ints.Add( 7 );
    }
    Console.WriteLine( v );
    first = false;
}
Run Code Online (Sandbox Code Playgroud)

如果你注释掉内部for循环,它会抛出,显然是因为我们对集合进行了更改.

现在,如果你取消注释,为什么这个循环允许我们添加这两个项目?它需要一段时间才能运行半分钟(在奔腾CPU上),但它不会抛出,而有趣的是它输出:

图片

这有点预期,但它表明我们可以改变,它实际上改变了收藏.任何想法为什么会发生这种行为?

Jon*_*eet 119

问题在于List<T>检测修改的方法是保留类型的版本字段,int在每次修改时递增它.因此,如果你做了准确的2次某个倍数32修改,迭代之间的列表,它会尽可能的检测而言渲染无形的这些修改.(它将从溢出int.MaxValueint.MinValue,并最终回到其初始值.)

如果你几乎改变了你的代码 - 添加1或3个值而不是2,或者将内循环的迭代次数减少1,那么它将按预期抛出异常.

(这是一个实现细节,而不是指定的行为 - 它是一个实现细节,可以在非常罕见的情况下被视为一个错误.但是,看到它在真实程序中导致问题是非常不寻常的.)

  • @LyingOnTheSky:不,我喜欢在跟随和批评C#语言方面扮演语言设计师的角色.我也是ECMA-334技术小组的标准化C#5 ...所以我选择了漏洞,但没有做真正的语言设计工作:) (6认同)
  • 仅供参考:[相关源代码](http://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs#1224),请注意`_version`字段是`int`. (5认同)
  • 我不确定这应该被称为"实现细节",因为该实现决策存在副作用,即使不太可能发生,也是真实的.规范(或至少是doc)说它应该抛出一个`InvalidOperationException`,实际上并不总是如此.当然,这取决于"实施细节"的定义. (4认同)
  • Jon Skeet,你是​​编程语言设计师吗?(在Google上没有找到任何相关内容)有点好奇为什么你也有这方面的知识.这个问题有点儿可以看到Stack Overflow的"权力". (3认同)