前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >我大意了,没有闪。

我大意了,没有闪。

作者头像
有态度的马甲
发布于 2023-09-05 10:47:59
发布于 2023-09-05 10:47:59
24900
代码可运行
举报
文章被收录于专栏:精益码农精益码农
运行总次数:0
代码可运行
本文的诞生,是有感于一线码农大佬前几日公众号发文《Dictionary.Clea
1. 无心插花
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void Example1()
{
  var newDict = new Dictionary<string, string>();
  newDict.Add("key1", "value1");
  newDict.Add("key2", "value2");
  foreach (var item in newDict)
  {
    newDict = new Dictionary<string, string>();  // 这里重新赋值
    Console.WriteLine($"new : {item}");
  }
}

void  Example2() 
{
    var newDict= new Dictionary<string, string>();
    newDict.Add("key1", "value1");
    newDict.Add("key2", "value2");
    foreach (var item in newDict)
    {
        newDict.Clear();       // 这里修改了原引用的数据,为啥不报错?
        Console.WriteLine($"clear : {item}");
    }
}

这两个栗子输出的是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
new : [key1, value1]
new : [key2, value2]
clear : [key1, value1]

这个输出是不是很奇怪:

(1) 栗子1重新new赋值难道不是修改了原字典对象newDict吗?foreach字典为什么不报InvalidOperation异常?

(2) 栗子2都肉眼可见的Clear字典了,foreach字典为什么还不报InvalidOperation异常?

2. Example1:抓的是周树人,与我鲁迅何干?

这个问题我大意了,没有闪😱😱。

这个问题其实与foreach没深入关系,其实就是多引用指向同一区域的问题,还是说下流程吧。

(1) 对字典做foreach, 内部会利用原对象newDict产生一个Enumerator迭代器。 IDictionaryEnumerator IDictionary.GetEnumerator() => new Enumerator(this, Enumerator.DictEntry);

https://github.com/dotnet/runtime/blob/45acd380b37c9ee883070a70a2ef2cb7eca77683/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs#L1331

有关foreach本质,强烈推荐看这个:2021年了,IEnumeratorIEnumerable还傻傻分不清楚?[1]。

(2) 关键是迭代器使用的新的readonly Dictionary<TKey, TValue> _dictionary;字段指向了原newDict指向的对象。

https://github.com/dotnet/runtime/blob/45acd380b37c9ee883070a70a2ef2cb7eca77683/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs#L1367

(3) 于是在外部尝试重置newDict,与_dictionary无关,故会出现上面看似诡异的效果。

抓的是周树人,与我鲁迅何干。


3. Example2:.Net Core3.0+ breakChange

Example2肉眼可见地在foreach内变更了原迭代对象,竟然不报InvalidOperationException

这个问题说来话长,真的说来话长。😪😪

循着源码看迭代器报InvalidOperationException异常的时机、查看字典Clear方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// https://github.com/dotnet/runtime/blob/64243bbf5e9ee53c0c4c5678f2cd8c7f1c9b4f6f/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs#L1385
if (_version != _dictionary._version)
{
     ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
}
  
// https://github.com/dotnet/runtime/blob/cf258a14b70ad9069470a108f13765e0e5988f51/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs#L223C5-L238C10
public void Clear()
{
    int count = _count;
    if (count > 0)
    {
        Debug.Assert(_buckets != null, "_buckets should be non-null");
        Debug.Assert(_entries != null, "_entries should be non-null");

        Array.Clear(_buckets, 0, _buckets.Length);

        _count = 0;
        _freeList = -1;
        _freeCount = 0;
        Array.Clear(_entries, 0, count);
     }
}

静态分析源码,貌似Dictionary认定字典正在变更的关键是verison字段发生变化Clear()字典清空了原键值对、count、空闲空间等字段,确实没引起version字段变化。

围观微软官方Dictionary信源[2]:

属性 Count 设置为 0,并且也会释放对集合元素中其他对象的引用。容量保持不变。

此方法是 O (n) 操作,其中 n 是字典的容量。

仅限 .NET Core 3.0+ :可以安全地调用此可变方法,而不会使实例上的 Dictionary<TKey,TValue> 活动枚举器失效。这并不表示线程安全。


技能点:食之无用弃之可惜

ok, That'all, 这是看一线码农大佬昨日分享《DictionaryClear和newDictionary有什么不同[3]》的一点补充,[把原文给出的字典Example改成List Example]那又是一个有意思的话题,暂时不表,读者自行尝试。

本文没啥有用技能点,😓😓

一个是多引用指向同一片空间,另一个是源码逻辑的breakChange,不成体系,食之无用弃之可惜。

预告:今日既然聊到了C#字典,字典也是必考八股文,我会抽时间温习C# Dictionary的实现并给出自己的理解。

最后啰嗦一句:全文原创,希望得到各位反馈,欢迎斧正交流, 若有更多进展,会实时更新到[左下角阅读原文]。

引用链接

[1] 2021年了,`IEnumerator`、`IEnumerable`还傻傻分不清楚?: https://www.cnblogs.com/JulianHuang/p/14271285.html

[2] 微软官方Dictionary信源: https://learn.microsoft.com/zh-cn/dotnet/api/system.collections.generic.dictionary-2.clear?view=net-7.0#system-collections-generic-dictionary-2-clear

[3] Dictionary Clear和new Dictionary有什么不同: https://mp.weixin.qq.com/s/JUtr9TFRDfAvEeu6vJkI1w

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-08-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 精益码农 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
C#创建安全的字典(Dictionary)存储结构
彭泽0902
2018/01/04
2.5K0
站在前人的肩膀上重新透视C# Span<T>数据结构
先谈一下我对Span的看法, Span是指向任意连续内存空间的类型安全、内存安全的视图,可操作的滑动窗口。
有态度的马甲
2022/08/23
3510
站在前人的肩膀上重新透视C# Span<T>数据结构
.NET Core跨平台的奥秘[下篇]:全新的布局
从本质上讲,按照CLI规范设计的.NET从其出生的那一刻就具有跨平台的基因,这与Java别无二致。由于采用了统一的中间语言,微软只需要针对不同的平台设计不同的虚拟机(运行时)就能弥合不同操作系统与处理器架构之间的差异,但是“理想很丰满,现实很骨感”。在过去十多年中,微软将.NET引入到了各个不同的应用领域,表面上看起来似乎欣欣向荣,但是由于采用完全独立的多目标框架的设计思路,导致针对多目标框架的代码平台只能通过PCL(参考《.NET Core跨平台的奥秘[中篇]:复用之殇》)这种“妥协”的方式来解决。如果依
蒋金楠
2018/02/08
1.1K0
.NET Core跨平台的奥秘[下篇]:全新的布局
C#:数据结构queue队列源码:循环使用数组头标,尾标,防止不停扩容数组
https://referencesource.microsoft.com/#mscorlib/system/collections/queue.cs
立羽
2023/08/24
1880
C#反射与特性(二):探究反射
在上一章中,我们探究了 C# 引入程序集的各种方法,这一章节笔者将探究 C# 中使用反射的各种操作和代码实践。
痴者工良
2021/04/26
1.6K0
Objective-C中的集合类
       下面详细的介绍Objective-C中的集合类以及每个集合类的用法,学过其他面向对象编程语言的小伙伴们看到OC的集合类会有种莫名的亲切感,理解起来问题不大,可以类比Java中的集合类去学习。 在Objective-C中的集合类中主要包括不可变的数组--NSArray,  可变的数组--NSMutableArray,   不可变的字典--NSDictionary,    可变的字典--NSMutableDictionary, 不可变的集合--NSSet,可变的集合--NSMutableSet。
lizelu
2018/01/11
1.2K0
DotNet Dictionary 实现简介
本来笔者对DotNet的Hashtable及Dictionary认识一直集中在使用上,一个直接用object 一个可以用泛型,以前也只大概看过Hashtable的实现。最近查MSDN时发现有建议开发者使用Dictionary代替Hashtable的描述,出于好奇测试了Hashtable及Dictionary读写性能,发现无论读还是写Dictionary都大幅领先Hashtable,然后就花时间整理了Dictionary操作逻辑试图找到这种性能提升的原因(最后会发现实现上的差异带来的性能明显提升也算的上是理所当然)。下文实际是介绍的Dictionary的实现(调试中使用的源是corefx 3.1),其中穿插着对比了Hashtable的实现逻辑。
lulianqi
2022/03/11
3540
DotNet Dictionary 实现简介
C# 自定义可迭代类型
/// <summary> /// 自定义泛型可迭代类型 /// </summary> /// <example> /// This code shows how to build a instance of <see cref="SelfEnumerable"/>: /// <code> /// var enumerable = new SelfEnumerable<typeparam name="T">Person</typeparam>5); /// enumerable.Add(new Person
雪飞鸿
2020/04/01
4160
.NET 10首个预览版发布:重大改进与新特性概览!
.NET 团队于2025年2月25日发布博文,宣布推出 .NET 10 首个预览版更新,重点改进.NET Runtime、SDK、Libraries 、C#、ASP.NET Core、Blazor 和.NET MAUI 等。
追逐时光者
2025/03/05
4710
.NET 10首个预览版发布:重大改进与新特性概览!
C#泛型
泛型(Generic) 是C# 2.0中的新增元素。这种机制允许将类名作为参数传递给泛型类型,并生成相应的对象。将泛型(包括类、接口、方法等)看作模板可能更好理解,模板中的变体部分将被作为参数传进来的类名称所代替,从而得到一个新的类型定义。   通过泛型可以定义类型安全类,而不会损害类型安全、性能或工作效率。您只须一次性地将服务器实现为一般服务器,同时可以用任何类型来声明和使用它。为此,需要使用 <和 > 括号,以便将一般类型参数括起来。 List<string> list = new List<strin
拾点阳光
2018/05/10
1.7K0
自定义Dictionary支持线程安全
本文转载:http://www.cnblogs.com/kiddo/archive/2008/09/25/1299089.html
跟着阿笨一起玩NET
2018/09/19
1.1K0
ConcurrentDictionary字典操作竟然不全是线程安全的?
标题不准确,实际上ConcurrentDictionary<TKey,TValue>绝大部分api都是线程安全且原子性的[1], 唯二的例外是接收工厂函数的api:AddOrUpdate、GetOrAdd,这两个api不是原子性的,需要引起重视。
有态度的马甲
2022/12/21
6090
ConcurrentDictionary字典操作竟然不全是线程安全的?
C# 13 中的 OverloadResolutionPriorityAttribute
C# 13 引入了 params collection 的 feature,可以参考我们之前的介绍 C# 13 新特性 params collection,不过有一个问题,我们之前也有提到就是如果我们要针对原来的数组新增 ReadOnlySpan 的重载可能会发生破坏性的变更,原来调用数组方法可能会变成调用 ReadOnlySpan 的方法重载,所以后面引入了 OverloadResolutionPriorityAttribute 来控制方法重载解析的优先级这样开发者可以为原有的方法指定一个较高的优先级来保证不会 break,之前我们也提到过这个 attribute 不过之前编译器还没支持,现在已经支持了
JusterZhu
2025/01/23
440
C# 13 中的  OverloadResolutionPriorityAttribute
.net源码分析 – Dictionary<TKey, TValue>
接上篇:.net源码分析 – List<T> Dictionary<TKey, TValue>源码地址:https://github.com/dotnet/corefx/blob/master/src
用户1147588
2018/01/04
1.8K0
.net源码分析 – Dictionary<TKey, TValue>
C#之 Dictionary 详解
Dictionary<TKey, TValue>是C#中用于存储键值对集合的泛型类,属于System.Collections.Generic命名空间。它允许使用键(Key)来访问与其关联的值(Value)。其中,TKey表示字典中键的类型,TValue表示字典中值的类型。
追逐时光者
2025/03/07
1250
【C# 基础精讲】字典(Dictionary)的使用
在C#中,Dictionary<TKey, TValue>是一种非常常用的泛型集合类,用于存储键值对(Key-Value Pair)的数据结构。Dictionary<TKey, TValue>可以根据键快速查找对应的值,因此在需要快速查找和检索数据的场景下,特别是在涉及大量数据时,使用字典是非常高效的选择。本文将详细介绍Dictionary<TKey, TValue>的应用,包括创建字典、添加元素、访问元素、删除元素、遍历字典、常用的方法等内容。
繁依Fanyi
2023/10/12
1.7K0
【C# 基础精讲】字典(Dictionary)的使用
C#基数排序算法
基数排序(Radix Sort)是一种非比较型整数排序算法,其基本思想是将整数按位数切割成不同的数字,然后按每个位数分别比较。这个算法在处理大量数据时非常有效,尤其是当数据的范围很大时。基数排序的时间复杂度通常为O(nk),其中n是待排序数组中的元素数量,k是数组中最大数的位数。
Michel_Rolle
2024/10/10
2.6K0
调试 .NET Core 中的内存泄漏
当应用引用不再需要执行所需任务的对象时,可能会发生内存泄漏。 引用上述对象会使垃圾回收器无法回收所使用的内存,这通常会导致性能降低,并可能最终引发 OutOfMemoryException。
呆呆
2022/01/09
1.8K0
dotnet C# 字典 Dictionary 和 Hashtable 的性能对比
如果没有特别的需求,请使用 Dictionary 而不是 Hashtable 原因是 Dictionary 的性能更好,本文将告诉大家 Stephen Toub 大佬的评测
林德熙
2021/12/24
7010
dotnet C# 字典 Dictionary 和 Hashtable 的性能对比
.net源码分析 – List<T>
通过分析源码可以更好理解List<T>的工作方式,帮助我们写出更稳定的代码。 List<T>源码地址: https://github.com/dotnet/corefx/blob/master/sr
用户1147588
2018/01/04
7420
.net源码分析 – List<T>
相关推荐
C#创建安全的字典(Dictionary)存储结构
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验