Skip to content

Let concurrent and read-only dictionaries call DeepClone() without a cast - #84

Merged
matt-edmondson merged 1 commit into
mainfrom
fix/81-concurrent-readonly-dictionary-deepclone
Sep 27, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
fix/81-concurrent-readonly-dictionary-deepclone

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #81

What was wrong

ConcurrentDictionary<,> and ReadOnlyDictionary<,> implement both IDictionary<,> and IReadOnlyDictionary<,>, so calling DeepClone() on either one failed to compile with CS0121 (ambiguous call). #79 already fixed this for Dictionary<,> and SortedDictionary<,>. These two types were still left.

Change

  • Added a DeepClone(this ConcurrentDictionary<,>) overload that returns a ConcurrentDictionary<,>. On .NET 6 and later it keeps the source's key comparer. The .NET Standard targets can't read ConcurrentDictionary.Comparer, so on those the clone uses the default comparer. The XML doc says so.
  • Added a DeepClone(this ReadOnlyDictionary<,>) overload that returns a new ReadOnlyDictionary<,> wrapping a deep-cloned dictionary.
  • Removed the cast workarounds at DeepCloneTests.cs ("Cast to IDictionary to resolve ambiguity") and SpecializedCollectionTests.cs ("explicit cast to IDictionary to resolve ambiguity").

Tests

  • ConcurrentDictionary_DeepClone_ShouldCreateIndependentCopy now calls original.DeepClone() directly.
  • New: ConcurrentDictionary_DeepClone_ShouldKeepComparer and ReadOnlyDictionaryWrapper_DeepClone_ShouldCreateIndependentCopy.
  • With the library change reverted, the test project fails to build with CS0121 at all three call sites. With the change in place, dotnet test passes 44/44.

Not in scope

ImmutableDictionary<,> and ImmutableSortedDictionary<,> also implement both interfaces, so they would hit the same ambiguity. Cloning them through IDictionary<,> would also call Add on an immutable type. They need their own design, so they are left for a separate issue. #83 (comparer loss through the IDictionary path for SortedList and others) is also separate.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KaXGMeYeXB1J32kSrLGhgJ


Generated by Claude Code

…cast

ConcurrentDictionary<,> and ReadOnlyDictionary<,> implement both
IDictionary<,> and IReadOnlyDictionary<,>, so dictionary.DeepClone() was an
ambiguous call (CS0121) for them. Add concrete overloads, as Dictionary<,>
and SortedDictionary<,> already have. The ConcurrentDictionary overload keeps
the key comparer on .NET 6 and later, where the type exposes it.

Remove the cast workarounds from the tests.

Fixes #81

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KaXGMeYeXB1J32kSrLGhgJ
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 7468b2c into main Sep 27, 2026
12 checks passed
@matt-edmondson
matt-edmondson deleted the fix/81-concurrent-readonly-dictionary-deepclone branch September 27, 2026 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants