CSHARP-6024: Support ReadOnlySet Serialization#1987
Conversation
| return value; | ||
| } | ||
|
|
||
| // last resort: try a constructor that takes a HashSet<TItem> (e.g. ReadOnlySet<T>(ISet<T>)) |
There was a problem hiding this comment.
I've put this as the last possibility in the method so that we do not cause issues with preexisting code.
There was a problem hiding this comment.
Pull request overview
Adds support for deserializing set-shaped IEnumerable<T> implementers (including .NET 9+ ReadOnlySet<T>) by introducing a HashSet<T>-based constructor fallback in EnumerableInterfaceImplementerSerializer, along with new unit tests covering constructor selection and ReadOnlySet<T> roundtripping.
Changes:
- Updated
EnumerableInterfaceImplementerSerializer<TValue, TItem>.FinalizeResultto add a last-resortHashSet<TItem>constructor fallback (enablingISet<T>-constructor-only targets). - Added tests to verify: (1) set-ctor fallback behavior, (2) duplicate-collapsing behavior for set-shaped targets, (3) preference for
IEnumerable<T>ctor when available. - Added a .NET 9+ roundtrip test for
System.Collections.ObjectModel.ReadOnlySet<T>.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| tests/MongoDB.Bson.Tests/Serialization/Serializers/EnumerableInterfaceImplementerSerializerTests.cs | Adds regression/behavior tests for set-constructor fallback and .NET 9+ ReadOnlySet<T> roundtrip. |
| src/MongoDB.Bson/Serialization/Serializers/EnumerableInterfaceImplementerSerializer.cs | Implements HashSet<TItem>-based constructor fallback in FinalizeResult to support set-shaped targets like ReadOnlySet<T>. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // last resort: try a constructor that takes a HashSet<TItem> (e.g. ReadOnlySet<T>(ISet<T>)) | ||
| // Note: collapses duplicate elements, which is correct for set-shaped targets. |
|
|
||
| // last resort: try a constructor that takes a HashSet<TItem> (e.g. ReadOnlySet<T>(ISet<T>)) | ||
| // Note: collapses duplicate elements, which is correct for set-shaped targets. | ||
| var setCtor = FindSingleArgumentConstructor(typeof(HashSet<TItem>)); |
There was a problem hiding this comment.
I think the change from HashSet<TItem> to ISet<TItem> went in the wrong direction.
Is the intent of this fallback not to "find any single-argument constructor that a HashSet can satisfy"?
In which case the right probe is therefore typeof(HashSet<TItem>), not typeof(ISet<TItem>). The difference matters because HashSet<T> implements IReadOnlySet<T> (added in .NET 5), but ISet<T> does not. A type with a constructor taking IReadOnlySet<T> would work with HashSet<TItem> as the probe but silently fall through to the exception with ISet<TItem>. No such type exists in the BCL today, but the HashSet<TItem> probe is strictly more correct as the intent.
There was a problem hiding this comment.
@papafe @sanych-sun any thoughts on my comment above?
There was a problem hiding this comment.
Yep, I think we should try to find a constructor for the actual type we're using.
There was a problem hiding this comment.
I think we should decide on #1987 (comment) comment first, because it might change the way we approach the problem.
|
|
||
| // exercises the HashSet<TItem> ctor fallback in FinalizeResult: target type with | ||
| // only an ISet<TItem> constructor — same shape as ReadOnlySet<T> | ||
| public class SetOnlyCtorEnumerable<T> : IEnumerable<T> |
There was a problem hiding this comment.
Do we really need this mock as we have net10.0 target for tests and can exercise with the ReadOnlySet<> itself?
There was a problem hiding this comment.
Agreed, removed this one.
adelinowona
left a comment
There was a problem hiding this comment.
FinalizeResult currently seems to do reflection on every deserialization call to figure out how to construct TValue, but the answer is fixed for any given TValue/TItem pair. A static readonly field on the generic class could compute this once per concrete type thus saving us a reflection cost. @sanych-sun WDYT?
|
About |
|
@sanych-sun Regarding |
|
@adelinowona In our codebase - yes, but we are talking about changes in public base class, there might be another implementations out of our control. |
|
@sanych-sun yep, my proposed change is entirely contained within |
No description provided.