Search code examples
c#collectionsienumerable

Why doesn't IEnumerator.MoveNext throw when removing keys from a ConditionalWeakTable?


The following code does not throw, even though the enumerated collection is modified during enumeration.

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]

Is this by design, or chance? If the former, then what would stop an exception from being thrown in the case where a key gets garbage collected?

Many thanks!


Solution

  • By design.

    Citing from the docs.

    The returned enumerator does not extend the lifetime of any object pairs in the table, other than the current one. It does not return entries that have already been collected or that were added after the enumerator was retrieved. Additionally, it may not return all entries that were present when the enumerator was retrieved, for example, entries that were collected or removed after the enumerator was retrieved but before they were enumerated.

    The object that is being pointed to by the enumerator's Current property will not get garbage collected and can be safely accessed. If a key further in the enumerable gets GCed or removed, the enumerator will simply not access that element.

    As mentioned by canton7 in the comments, this is required for the ConditionalWeakTable to remain thread-safe - other threads might be removing elements during another thread's enumeration.