.net 5中的更改使在foreach中更改字典值时不会抛出该错误 [英] What changed in .net 5 that makes it not throw when changing dictionary values in foreach
问题描述
在.NET< 5和.NET Core 3.1中,以下代码
var d = new字典< string,int>{{"a",0},{"b",0},{"c",0}};foreach(d.Keys中的var k){d [k] + = 1;}
投掷
System.InvalidOperationException:集合已修改;枚举操作可能无法执行.
定位.NET 5时,该代码段不再抛出.
发生了什么变化?
我无法在 .NET 5中发生重大变化的情况下找不到答案和 .NET 5中的性能改进./p>
与 ref只读T
有关吗?
Dictionary< TKey,TValue>
的源代码进行了更改,以允许更新现有密钥在枚举期间.它是由Stephen Toub在2020年4月9日提交的.该提交可以在 PR#34667 .
PR的标题为在枚举期间允许字典覆盖".并注意到它修复了 issue#34606 考虑删除 _version ++
覆盖 Dictionary< TKey,TValue>
''中的内容.图卜先生发表的那个问题的案文如下:
我们以前从删除'
_version ++
字典.覆盖时我们也应该考虑这样做字典中现有键的值.这将使更新循环以调整字典中的值,而无需诉诸复杂且昂贵的措施.
对此问题的评论要求:
这样做有什么好处?
斯蒂芬·托布(Stephen Toub)回复:
如原始帖子中所述,例如,当前正在抛出的精细模式将开始正常运行,例如
foreach(字典中的KeyValuePair< string,int>对)
dict [pair.Key] = pair.Value +1;
如果您查看 Dictionary<,>
TryInsert
方法(由索引器调用,请参见下文)及其类型为 InsertionBehavior
的第三个参数.当此值为 InsertionBehavior.OverwriteExisting
时,不会为 existing 键更新版本控制字段.
例如,请参见 优先,该部分的外观如下所示(代码注释): 请注意, 出于完整性考虑, 从字典中 Is it something to do with There was a change to the source code of The PR is titled "Allow Dictionary overwrites during enumeration" and notes that it fixes issue #34606 "Consider removing We previously removed the A comment on that issue asks: What is the benefit of doing this? To which Stephen Toub replied: As called out in the original post, fine patterns that are currently throwing today will start working correctly, e.g. If you look at the The area of particular interest is the For example, see this section of code from the updated Prior to the change that section looked like this (code comment mine): Note that the increment of the For completeness, the setter of the indexer looks like this. It was not modified by this change, but note the third parameter which influences the above behavior: .NET Core 3.0+ only: this mutating method may be safely called without
invalidating active enumerators on
the Despite one developer's insistence in the linked issue that the documentation be updated (and what appears to be an assurance that it would be), the docs for the indexer have not yet (2021-04-04) been updated to reflect the current behavior. 这篇关于.net 5中的更改使在foreach中更改字典值时不会抛出该错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋! if(行为== InsertionBehavior.OverwriteExisting){entrys [i] .value =值;返回true;}
if(行为== InsertionBehavior.OverwriteExisting){entrys [i] .value =值;_version ++;//< -----返回true;}
_version
字段的增量已删除,因此可以在枚举期间进行修改. set{布尔修改= TryInsert(键,值,InsertionBehavior.OverwriteExisting);Debug.Assert(修改);}
删除
'也不再影响枚举.但是,从netcore 3.0开始就存在这种情况,并且在Breaking changes in .NET 5 and Performance Improvements in .NET 5.ref readonly T
?Dictionary<TKey, TValue>
to allow updates of existing keys during enumeration. It was commited on April 9, 2020 by Stephen Toub. That commit can be found here along with corresponding PR #34667._version++
from overwrites in Dictionary<TKey, TValue>
". The text of that issue, opened by Mr. Toub is as follows:
_version++
when Remove'ing from a
dictionary. We should consider doing so as well when just overwriting
a value for an existing key in the dictionary. This would enable
update loops that tweak a value in the dictionary without needing to
resort to convoluted and more expensive measures.
foreach (KeyValuePair<string, int> pair in dict)
dict[pair.Key] = pair.Value + 1;
Dictionary<, >
source code, you can see that the _version
field (which is used to detect modifications) is now only updated under certain conditions and not when an existing key is modified.TryInsert
method (which is called by the indexer, see below) and its third parameter of type InsertionBehavior
. When this value is InsertionBehavior.OverwriteExisting
the versioning field is not updated for an existing key.TryInsert
:if (behavior == InsertionBehavior.OverwriteExisting)
{
entries[i].value = value;
return true;
}
if (behavior == InsertionBehavior.OverwriteExisting)
{
entries[i].value = value;
_version++; // <-----
return true;
}
_version
field has been removed, thus allowing modifications during enumeration.set
{
bool modified = TryInsert(key, value, InsertionBehavior.OverwriteExisting);
Debug.Assert(modified);
}
Remove
'ing from the dictionary no longer impacts enumeration either. That, however, has been around since netcore 3.0 and is appropriately called out in the documentation of Remove
:
Dictionary<TKey,TValue>
instance. This does not imply thread
safety.