You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
DeepCloneFrom / IEnumerable.DeepClone share dictionary values with the source when it is not an IDictionary (e.g. IReadOnlyDictionary, LINQ over a dictionary) #82
When the source of a clone is a sequence of KeyValuePair<TKey, TValue> but not statically an IDictionary<TKey, TValue>, overload resolution picks the element-wise overloads with T = KeyValuePair<TKey, TValue>:
A KeyValuePair<,> never implements IDeepCloneable, so the pair is copied as-is and the key and value objects are shared with the source. Nothing errors.
The same thing happens with any IEnumerable<KeyValuePair<,>> source, such as an IReadOnlyDictionary, ImmutableDictionary exposed through an interface, or a LINQ projection over a dictionary.
Why it matters
The caller asked for a deep clone and got one back without any error, but the values are the original instances. Mutating the "clone" then mutates the source. This is the kind of aliasing bug the library exists to prevent. The IDictionary overload of DeepCloneFrom does clone both key and value, so the result depends on the static type the caller happened to hold.
Suggested fix
Either of these works:
In the private helper, detect KeyValuePair<TKey, TValue> and rebuild the pair from DeepClone(pair.Key) and DeepClone(pair.Value).
Priority: High. A "deep clone" silently shares its values with the source, so mutating the clone mutates the original. That defeats the library's core guarantee, and it depends only on the static type the caller happens to hold (IReadOnlyDictionary, LINQ over a dictionary).
Area / suggested owner:DeepCloneContainerExtensions, the element-wise overloads and the private DeepClone<T> helper (maintainer: matt-edmondson).
Duplicates / in progress: None found. No open PR covers this.
Next step: Rebuild KeyValuePair<TKey, TValue> from deep-cloned key and value in the private helper, and add regression tests for an IReadOnlyDictionary source and a LINQ-filtered source.
Activity
matt-edmondson commentedon Sep 27, 2026
Triage
IReadOnlyDictionary, LINQ over a dictionary).DeepCloneContainerExtensions, the element-wise overloads and the privateDeepClone<T>helper (maintainer: matt-edmondson).IEnumerable<KeyValuePair<,>>overloads must not reintroduce that ambiguity, which makes the private-helperKeyValuePairdetection the lower-risk option. Also related: IEnumerable<T>.DeepClone() returns a lazy view: edits to the cloned items are lost, and the clone changes whenever the source changes #80 (IEnumerable.DeepClone()returns a lazy view), since the fix will touch the same overload.KeyValuePair<TKey, TValue>from deep-cloned key and value in the private helper, and add regression tests for anIReadOnlyDictionarysource and a LINQ-filtered source.Generated by Claude Code