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

查看:13
本文介绍了.net 5 中的哪些更改使其在更改 foreach 中的字典值时不会抛出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 .NET<5 和 .NET Core 3.1 中,以下代码

In .NET<5 and .NET Core 3.1 the following code

var d = new Dictionary<string, int> { { "a", 0 }, { "b", 0 }, { "c", 0 } };
foreach (var k in d.Keys) 
{
   d[k]+=1;
}

投掷

System.InvalidOperationException:集合被修改;枚举操作可能不会执行.

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.

当面向 .NET 5 时,代码段不再抛出.

When targeting .NET 5 the snippet no longer throws.

发生了什么变化?

我未能在 .NET 5 中的重大更改中找到答案.NET 5 中的性能改进.

是否与ref readonly T有关?

推荐答案

Dictionary 的源代码发生了变化,以允许更新现有键em> 在枚举过程中.它由 Stephen Toub 于 2020 年 4 月 9 日提交.也就是说提交可以发现这里与相应的的沿=HTTPS://github.com/dotnet/runtime/pull/34667" rel="noreferrer">PR #34667.

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.

PR 的标题是在枚举期间允许字典覆盖";并指出它修复了issue #34606考虑删除_version++ 来自 Dictionary 中的覆盖".Toub先生开出的那一期的正文如下:

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:

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

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.

对该问题的评论要求:

这样做有什么好处?

Stephen Toub 答复:

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 字典中的对) dict[pair.Key] = pair.Value + 1;

如果您查看字典<, > 源代码,可以看到_version字段(用于检测修改)现在是仅在特定条件下更新,不会在现有密钥被修改时更新.

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.

特别感兴趣的领域是 TryInsert 方法(由索引器调用,见下文)及其 InsertionBehavior 类型的第三个参数.当此值为 InsertionBehavior.OverwriteExisting 时,现有 键的版本控制字段不会更新.

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.

例如,请参阅此 代码部分来自更新 TryInsert:

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;
}

请注意,_version 字段的增量已被删除,因此允许在枚举期间进行修改.

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 从字典中删除也不再影响枚举.但是,自 netcore 3.0 以来就已经存在,并且在 Remove 的文档:

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+:可以安全地调用此变异方法,而无需使活动枚举器无效Dictionary 实例.这并不意味着线程安全.

.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.

尽管一位开发人员坚持要更新文档的链接问题(并且似乎可以保证它),但索引器的文档还没有 (2021-04-04) 已更新以反映当前行为.

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天全站免登陆