.net 5中的更改使在foreach中更改字典值时不会抛出该错误 [英] What changed in .net 5 that makes it not throw when changing dictionary values in foreach

查看:52
本文介绍了.net 5中的更改使在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日提交的.该提交可以在 issue#34606 考虑删除 _version ++ 覆盖 Dictionary< TKey,TValue> ''中的内容.图卜先生发表的那个问题的案文如下:

我们以前从删除' _version ++ 字典.覆盖时我们也应该考虑这样做字典中现有键的值.这将使更新循环以调整字典中的值,而无需诉诸复杂且昂贵的措施.

对此问题的评论要求:

这样做有什么好处?

斯蒂芬·托布(Stephen Toub)回复:

如原始帖子中所述,例如,当前正在抛出的精细模式将开始正常运行,例如

foreach(字典中的KeyValuePair< string,int>对) dict [pair.Key] = pair.Value +1;

如果您查看 Dictionary<,> 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.

Is it something to do with ref readonly T?

解决方案

There was a change to the source code of 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.

The PR is titled "Allow Dictionary overwrites during enumeration" and notes that it fixes issue #34606 "Consider removing _version++ from overwrites in Dictionary<TKey, TValue>". The text of that issue, opened by Mr. Toub is as follows:

We previously removed the  _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.

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.

foreach (KeyValuePair<string, int> pair in dict) dict[pair.Key] = pair.Value + 1;

If you look at the 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.

The area of particular interest is the 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.

For example, see this section of code from the updated TryInsert:

if (behavior == InsertionBehavior.OverwriteExisting)
{ 
    entries[i].value = value;
    return true;
}

Prior to the change that section looked like this (code comment mine):

if (behavior == InsertionBehavior.OverwriteExisting)
{ 
    entries[i].value = value;
    _version++; // <-----
    return true;
}

Note that the increment of the _version field has been removed, thus allowing modifications during enumeration.

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:

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:

.NET Core 3.0+ only: this mutating method may be safely called without invalidating active enumerators on the Dictionary<TKey,TValue> instance. This does not imply thread safety.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆