【问题标题】:Why doesn't IEnumerator.MoveNext throw when removing keys from a ConditionalWeakTable?为什么从 ConditionalWeakTable 中删除键时 IEnumerator.MoveNext 不抛出?
【发布时间】:2022-01-06 03:18:48
【问题描述】:

即使在枚举过程中修改了枚举集合,以下代码也不会抛出。

using System;
using System.Collections;
using System.Runtime.CompilerServices;

namespace ConsoleApp1
{
    class MyKey
    {

    }

    class Program
    {
        static void Main(string[] args)
        {
            ConditionalWeakTable<MyKey, string> table = new();

            MyKey k1 = new();
            MyKey k2 = new();
            MyKey k3 = new();

            table.Add(k1, "v1");
            table.Add(k2, "v2");
            table.Add(k3, "v3");

            var enumerator = ((IEnumerable)table).GetEnumerator();

            while(enumerator.MoveNext()) // no exception thrown
            {
                Console.WriteLine(enumerator.Current);

                table.Remove(k1);
                table.Remove(k2);
                table.Remove(k3);
            }
        }
    }
}
[ConsoleApp1.MyKey, v1]

这是有意为之,还是偶然?如果是前者,那么在密钥被垃圾回收的情况下,什么会阻止抛出异常?

非常感谢!

【问题讨论】:

  • ConditionalWeakTablethread safe。这意味着在您的线程迭代集合时可能有另一个线程正在删除项目,并且它需要安全。枚举器在调用 MoveNext() 之间有 logic to handle entries being removed
  • docs also state that 此外,它可能不会返回检索枚举器时存在的所有条目,例如,在检索枚举器之后但之前收集或删除的条目枚举 所以你可以放心地假设它是设计使然。也与提供的代码@canton7 匹配。
  • @canton7 我会接受您的评论作为答案 - 我忽略了线程安全部分。
  • 很好奇为什么你认为它抛出异常......
  • @Charlieface 因为一堆常见类型的枚举器,最著名的是List&lt;T&gt;,所以呢?

标签: c# collections ienumerable


【解决方案1】:

按设计。

引用自the docs

返回的枚举器不会延长表中任何对象对的生命周期,除了当前的。它不返回已收集或检索枚举器后添加的条目。此外,它可能不会返回检索枚举器时存在的所有条目,例如,在检索枚举器之后但在枚举之前收集或删除的条目。

枚举器的Current 属性所指向的对象将不会被垃圾回收并且可以安全地访问。如果可枚举中的某个键被 GC 或删除,则枚举器将根本无法访问该元素。

正如 canton7 在 cmets 中所提到的,ConditionalWeakTable 需要这样做才能保持线程安全 - 其他线程可能会在另一个线程的枚举期间删除元素。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-23
    • 2019-03-27
    • 2011-05-26
    • 2023-03-30
    • 2018-01-21
    • 1970-01-01
    相关资源
    最近更新 更多