.NET Hashtable is roughly the same lousy bucketed hash table from C++ std::unordered_map. So, the hash table trick gets you to one of N linked lists (the buckets), and then you walk that linked list you found to see if a matching element is in the list.
Writing sometimes needs to "re-bucket" everything and during that process it's mayhem if you were stupid enough to be simultaneously looking something up (but in .NET this will be prevented†) but before and afterwards even write operations "just" add or remove elements to linked lists, which you're correct in a GC language it's not crucial to care that some reader may look at an element which you have meanwhile removed [in C++ that would be a use-after-free]
> Enumerating through a collection is intrinsically not a thread safe procedure. Even when a collection is synchronized, other threads can still modify the collection, which causes the enumerator to throw an exception. To guarantee thread safety during enumeration, you can either lock the collection during the entire enumeration or catch the exceptions resulting from changes made by other threads.
So, you know, congratulations you played silly games and you have won yourself a stupid prize.
This has basically the same "cool uses" as with an RwLock, AFAIU the readers aren't actually allowed to look at the hash table while it is being rehashed, they're silently held by the implementation.
You could do the "Reclaim data only once nobody is looking" in a non-GC language with any of the other concurrent reclamation techniques such as RCU or Hazard Pointers and in many cases you'd be fine with a reference counting approach (not always which is why these other techniques exist).
Edited To Add: † Huh. Maybe under GC they get away with doing all the work and then just pivoting to the new hashtable after rebucketing with a single writer? So then readers just get obsolete data for a while and it's the poor GC which has to cope with the mess.