.Net5 下Dictionary 为什么可以在foreach中Remove
在一个讨论群里,看见有人说Dictionary可以在foreach中直接调用Remove了,带着疑问,写了简单代码进行尝试
class Program
{
static void Main(string[] args)
{ var dic = Enumerable.Range(1, 10).ToDictionary(t => t, t => t);
foreach (var i in dic)
{
if (i.Key.GetHashCode() % 2 == 0)
{
dic.Remove(i.Key);
}
else
{
Console.WriteLine($"{i.Key}");
}
}
Console.WriteLine("Hello World!");
}
}
执行果然没有报错,输出正常。

终于不再需要进行单独执行Remove
要想知道为啥在.Net Framework上不行,在.Net5下却可以,就需要知道在.Net5中Dictionary有着什么样的变化
我们看下两者有什么区别:
Framework中是这样的:

1 public bool MoveNext() {
2 if (version != dictionary.version) {
3 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
4 }
5
6 // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends.
7 // dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue
8 while ((uint)index < (uint)dictionary.count) {
9 if (dictionary.entries[index].hashCode >= 0) {
10 current = new KeyValuePair<TKey, TValue>(dictionary.entries[index].key, dictionary.entries[index].value);
11 index++;
12 return true;
13 }
14 index++;
15 }
16
17 index = dictionary.count + 1;
18 current = new KeyValuePair<TKey, TValue>();
19 return false;
20 }
.Net5中是这样的:

1 public bool MoveNext()
2 {
3 if (_version != _dictionary._version)
4 {
5 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
6 }
7
8 // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends.
9 // dictionary.count+1 could be negative if dictionary.count is int.MaxValue
10 while ((uint)_index < (uint)_dictionary._count)
11 {
12 ref Entry entry = ref _dictionary._entries![_index++];
13
14 if (entry.next >= -1)
15 {
16 _current = new KeyValuePair<TKey, TValue>(entry.key, entry.value);
17 return true;
18 }
19 }
20
21 _index = _dictionary._count + 1;
22 _current = default;
23 retur
细看好像两者并没什么很明显的区别。我们知道,在对Dictionary进行操作的时候,_version会自增改变,从而导致报错。难道.Net5中进行Remove操作_version不会改变。
.Net5中Remove代码:
1 public bool Remove(TKey key)
2 {
3 // The overload Remove(TKey key, out TValue value) is a copy of this method with one additional
4 // statement to copy the value for entry being removed into the output parameter.
5 // Code has been intentionally duplicated for performance reasons.
6
7 if (key == null)
8 {
9 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
10 }
11
12 if (_buckets != null)
13 {
14 Debug.Assert(_entries != null, "entries should be non-null");
15 uint collisionCount = 0;
16 uint hashCode = (uint)(_comparer?.GetHashCode(key) ?? key.GetHashCode());
17 ref int bucket = ref GetBucket(hashCode);
18 Entry[]? entries = _entries;
19 int last = -1;
20 int i = bucket - 1; // Value in buckets is 1-based
21 while (i >= 0)
22 {
23 ref Entry entry = ref entries[i];
24
25 if (entry.hashCode == hashCode && (_comparer?.Equals(entry.key, key) ?? EqualityComparer<TKey>.Default.Equals(entry.key, key)))
26 {
27 if (last < 0)
28 {
29 bucket = entry.next + 1; // Value in buckets is 1-based
30 }
31 else
32 {
33 entries[last].next = entry.next;
34 }
35
36 Debug.Assert((StartOfFreeList - _freeList) < 0, "shouldn't underflow because max hashtable length is MaxPrimeArrayLength = 0x7FEFFFFD(2146435069) _freelist underflow threshold 2147483646");
37 entry.next = StartOfFreeList - _freeList;
38
39 if (RuntimeHelpers.IsReferenceOrContainsReferences<TKey>())
40 {
41 entry.key = default!;
42 }
43
44 if (RuntimeHelpers.IsReferenceOrContainsReferences<TValue>())
45 {
46 entry.value = default!;
47 }
48
49 _freeList = i;
50 _freeCount++;
51 return true;
52 }
53
54 last = i;
55 i = entry.next;
56
57 collisionCount++;
58 if (collisionCount > (uint)entries.Length)
59 {
60 // The chain of entries forms a loop; which means a concurrent update has happened.
61 // Break out of the loop and throw, rather than looping forever.
62 ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported();
63 }
64 }
65 }
66 return false;
67 }
一看果然_version不会变化。看到这可能会直呼内行啊,一行代码就解决问题,那为什么Framework中不这样做呢。相关提交讨论
.Net5 下Dictionary 为什么可以在foreach中Remove的更多相关文章
- Java ArrayList在foreach中remove的问题分析
目录 iterator itr.hasNext 和 itr.next 实现 倒数第二个元素的特殊 如何避坑 都说ArrayList在用foreach循环的时候,不能add元素,也不能remove元素, ...
- .Net5下WebRequest、WebClient、HttpClient是否还存在使用争议?
WebRequest.WebClient.HttpClient 是C#中常用的三个Http请求的类,时不时也会有人发表对这三个类使用场景的总结,本人是HttpClient 一把梭,也没太关注它们的内部 ...
- C#实现在foreach中删除集合中的元素
List<string> str = new List<string>(); str.Add( "zs"); str.Add("ls") ...
- PHP在foreach中对$value赋值无效,应该用 ‘键’ 或者 &$value的形式
首先我们看下这段代码: foreach ($data as$value) { $value['name'] = 'Hehe'; } $data中原始的数据为: array(1) { [0] => ...
- js forEach参数详解,forEach与for循环区别,forEach中如何删除数组元素
壹 ❀ 引 在JS开发工作中,遍历数组的操作可谓十分常见了,那么像for循环,forEach此类方法自然也不会陌生,我个人也觉得forEach不值得写一篇博客记录,直到我遇到了一个有趣的问题,我们来 ...
- foreach中的collection
foreach中collection的三种用法 https://www.cnblogs.com/xiemingjun/p/9800999.html foreach的主要用在构建in条件中,它可以在SQ ...
- 如何在 Array.forEach 中正确使用 Async
本文译自How to use async functions with Array.forEach in Javascript - Tamás Sallai. 0. 如何异步遍历元素 在第一篇文章中, ...
- mac下查看.mobileprovision文件及钥匙串中证书.cer文件
mac下查看.mobileprovision文件及钥匙串中证书.cer文件 一. mobileprovision文件查看 xxx.mobileprovision是ios开发中的设备描述文件,里面有证书 ...
- BarTender如何将条码下的数字嵌入到条码中
现今社会,在各种包装箱子.书籍.超市商品等东西上面,必不可少的绝对要数条形码或者二维码了.有时候,根据客户的需求或者其他条件限制等原因,我们需要将BarTender 2016条码下的数字嵌入到条码中. ...
随机推荐
- redis键过期时间
redis服务器中每个数据库都是一个redisDb,而redisDb实质上是一个字典的模型,数据库的每一个键都是一个字典的键值,对数据库的增删改查也就是对字典对象的增删改查. redis在维护带有过期 ...
- cobaltstrike的使用
0x01 介绍 Cobalt Strike是一款渗透测试神器,常被业界人称为CS神器.Cobalt Strike已经不再使用MSF而是作为单独的平台使用,它分为客户端与服务端,服务端是一个,客户端可以 ...
- Sentry React SourceMaps All In One
Sentry React SourceMaps All In One React https://docs.sentry.io/platforms/javascript/guides/react/ h ...
- Design Patterns All in One (JavaScript Version)
Design Patterns All in One (JavaScript Version) JavaScript 设计模式 JavaScript 数据结构 23种设计模式分为 3 大类: 创建型模 ...
- PerformanceObserver API All In One
PerformanceObserver API All In One 性能监控 https://developer.mozilla.org/en-US/docs/Web/API/Performance ...
- 海 鱼立 鲷 & 海䲞鲷
海 鱼立 鲷 & 海䲞鲷 䲞 lì 鲷 diāo 二长棘鲷 二长棘鲷(学名:Parargyrops edita)为辐鳍鱼纲鲈形目鲷科二长棘鲷属的鱼类,俗名板鱼.䲞鱼.盘仔鱼.立花.赤鬃.长鳍. ...
- GitHub Sponsors
GitHub Sponsors https://github.com/sponsors https://github.com/sponsors/xgqfrms?preview=true https:/ ...
- 揭秘高倍矿币 Baccarat BGV,为何NGK DeFi的财富效应如此神奇?
作为区块链4.0代表的NGK公链,这次也将借助它自己的DeFi版块NGK Baccarat,开启属于它自己的千倍财富之旅. 如果说,比特币能让没有银行账户的人,可以在全球任何时间.地点都能自由进行交易 ...
- 备战春招!开源社区系统 Echo 超全文档助力面试
博主东南大学硕士在读,寒假前半个月到现在差不多一个多月,断断续续做完了这个项目,现在终于可以开源出来了,我的想法是为这个项目编写一套完整的教程,包括技术选型分析.架构分析.业务逻辑分析.核心技术点分析 ...
- Spirent Testcenter二层DHCP绑定流配置
1.OLT配置 配一个VLAN,若GE口打Tag,不需要打PVID,打Untag,配PVID. 在ONU上配一个Other Bridge的WAN连接,并配置VLAN 2.TestCenter配置 选定 ...