栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

为什么BCL集合使用结构枚举器而不是类?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

为什么BCL集合使用结构枚举器而不是类?

确实,这是出于性能原因。BCL团队在这一点上进行了 大量 研究,然后才决定采用您正确地称呼为可疑和危险的做法:使用可变值类型。

您问为什么这不会导致拳击。这是因为C#编译器不会避免将代码填充到foreach循环中的IEnumerable或IEnumerator中的东西!

当我们看到

foreach(X x in c)

我们要做的第一件事是检查c是否具有名为GetEnumerator的方法。如果是,那么我们检查一下它返回的类型是否具有方法MoveNext和属性current。如果是这样,那么将直接使用对这些方法和属性的直接调用来生成foreach循环。只有当“模式”无法匹配时,我们才回过头去寻找接口。

这具有两个理想的效果。

首先,如果该集合是int的集合,但是在发明泛型类型之前编写的,则它不会对将Current的值装箱到对象然后将其装箱到int进行装箱惩罚。如果Current是一个返回int的属性,则只需使用它。

其次,如果枚举数是值类型,则它不会将枚举数装箱到IEnumerator。

就像我说过的那样,BCL团队对此进行了很多研究,发现在大多数情况下,分配 和取消分配
枚举数的代价足够大,因此值得将其设为值类型,即使这样做可以导致一些疯狂的错误。

例如,考虑一下:

struct MyHandle : IDisposable { ... }...using (MyHandle h = whatever){    h = somethingElse;}

您很正确地期望使h突变的尝试失败,并且确实如此。编译器检测到您正在尝试更改待处理对象的值,并且这样做可能导致需要处理的对象实际上未被处理。

现在假设您有:

struct MyHandle : IDisposable { ... }...using (MyHandle h = whatever){    h.Mutate();}

这里会发生什么?您可能有理由希望,如果h是一个只读字段,则编译器将执行其操作:创建一个副本,并对副本进行突变,以确保该方法不会丢弃需要处置的值。

但是,这与我们对应该在此处发生的事情的直觉相冲突:

using (Enumerator enumtor = whatever){    ...    enumtor.MoveNext();    ...}

我们希望在using块内执行MoveNext 会将 枚举数移动到下一个枚举数,而不管它是struct类型还是ref类型。

不幸的是,今天的C#编译器存在一个错误。如果您处于这种情况,我们会选择不一致的策略。今天的行为是:

  • 如果通过方法进行变异的值型变量是普通局部变量,则通常进行变异

  • 但是,如果它是一个悬挂本地(因为它是一个匿名函数迭代器块的或在一个封闭变量),那么本地 作为只读字段实际产生,并且齿轮,其确保突变发生在副本上花费过度。

不幸的是,该规范在此问题上提供的指导很少。显然,由于我们的操作不一致,导致某些问题已损坏,但是 正确 的做法尚不清楚。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/576122.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号