Feature/readonly collections and adapters#140
Open
kasyanovandrii wants to merge 72 commits into
Open
Conversation
…attributes - Support IReadOnlyDictionary<TKey,TValue> in [DictionaryEquality] — generator now recognises both IDictionary and IReadOnlyDictionary via AllInterfaces check; generated DictionaryEquals helper uses IEnumerable<KeyValuePair<TKey,TValue>> as the common base and pattern-matches to IReadOnlyDictionary/IDictionary for O(1) TryGetValue lookup without casting - Add ReadOnlyDictionaryEqualityComparer<TKey,TValue> to Equatable.Comparers for use with [EqualityComparer] on nested IReadOnlyDictionary properties - Replace OrderBy in DictionaryHashCode and HashSetHashCode with commutative sum-of-HashCode.Combine approach — order-independent and allocation-free; same fix applied to DictionaryEqualityComparer and HashSetEqualityComparer - Add [DataContractEquatable] adapter attribute — reacts to [DataMember]/ [IgnoreDataMember] instead of [IgnoreEquality]; feeds into the same EquatableWriter pipeline - Add [MessagePackEquatable] adapter attribute — reacts to MessagePack [Key]/[IgnoreMember] attributes - Add entity and generator snapshot tests for all new scenarios Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…d collection composition - Add MultiDimensionalArrayEqualityComparer<T> for int[,]/T[,] with zero-allocation Array.GetEnumerator() iteration - Extend GetBaseEquatableType and HasEquatableAttribute to recognise DataContractEquatable and MessagePackEquatable bases so derived classes emit base.Equals()/base.GetHashCode() - Fix IsIncludedDataContract namespace check to require full System.Runtime.Serialization chain - Auto-compose nested collection comparers (SequenceEqualityComparer, DictionaryEqualityComparer, HashSetEqualityComparer) recursively from a single top-level attribute; explicit [EqualityComparer] overrides at any level - Add ComparerTypes.Expression path in writer for fully-composed comparer instance expressions - Extend analyzer to cover DataContractEquatable/MessagePackEquatable types and accept arrays for [SequenceEquality] - Add pinned MetadataReferences for DataMemberAttribute and KeyAttribute assemblies to fix load-order fragility in tests - Add property-based tests (FsCheck) and snapshot tests for all new scenarios Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Core EquatableGenerator no longer knows about DataContract or MessagePack. RegisterProvider is now public static so adapter generators can call it directly. GetBaseEquatableType detects any *EquatableAttribute in Equatable.Attributes namespace instead of hard-coding adapter names. New packages: - Equatable.SourceGenerator.DataContract — DataContractEquatableGenerator - Equatable.SourceGenerator.MessagePack — MessagePackEquatableGenerator - Equatable.Generator.DataContract — NuGet wrapper + DataContractEquatableAttribute - Equatable.Generator.MessagePack — NuGet wrapper + MessagePackEquatableAttribute DataContractEquatableAttribute and MessagePackEquatableAttribute moved out of Equatable.Generator into their respective adapter packages. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…kObject DataContractEquatableAnalyzer (EQ0020): warns when [DataContractEquatable] is present on a class without [DataContract] — DataMember attributes are silently ignored by DataContractSerializer without it. MessagePackEquatableAnalyzer (EQ0021): warns when [MessagePackEquatable] is present on a class without [MessagePackObject] — Key attributes are ignored by the MessagePack serializer without it. Also add [DataContract]/[MessagePackObject] to all test entities and generator test sources to reflect real-world usage accurately. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…r duplication Both adapter generators repeated the same indexer/accessibility guard. Moved to EquatableGenerator.IsPublicInstanceProperty (public static) so adapters call the shared helper instead of copying the logic. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Structs that don't define op_Equality (e.g. Nullable<T> wrapping a plain struct) would cause a CS0019 compile error in generated code because the generator unconditionally emitted == for all value types. HasEqualityOperator() now checks the underlying type for op_Equality before choosing ComparerTypes.ValueType, falling back to EqualityComparer<T>.Default. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
[DictionaryEquality] defaults to order-independent equality (key-lookup Equals, sum-of-pairs hash). [DictionaryEquality(ordered: true)] opts into order-sensitive equality: SequenceEqual on the key-value pair sequence for Equals, sequential HashCode.Add per pair for GetHashCode. Both modes are guaranteed in sync: - Plain types: single ComparerTypes.OrderedDictionary enum value drives both the Equals and GetHashCode switch arms in the writer. - Nested types: BuildCollectionComparerExpression now always emits an OrderedDictionaryEqualityComparer / OrderedReadOnlyDictionaryEqualityComparer instance for ordered properties, so both Equals and GetHashCode go through the same comparer object and can never diverge. Adds OrderedDictionaryEqualityComparer<TKey,TValue> and OrderedReadOnlyDictionaryEqualityComparer<TKey,TValue> to Equatable.Comparers. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ode, enable property tests - Fix equality contract violation: GetHashCode used order-dependent logic while Equals was order-independent for dictionaries and hashsets - DictionaryEqualityComparer/ReadOnlyDictionaryEqualityComparer: GetHashCode now uses commutative SUM (insertion-order independent, consistent with TryGetValue Equals) - HashSetEqualityComparer: GetHashCode now uses commutative SUM (consistent with SetEquals) - Add [DictionaryEquality(sequential: true)] mode: both Equals and GetHashCode use OrderBy(key) for deterministic key-sorted comparison - OrderedDictionary comparers: fix OrderBy sort comparer to use hash tiebreaker when IComparer<TKey> treats distinct keys as equal (e.g. culture-sensitive string sort) - Migrate property-based tests from FsCheck v2 to FsCheck.Xunit.v3 (xUnit v3 compatible) - Add Arbitraries class with custom generators for HashSet<T> (not auto-generated in v3) - Fix property test semantics for SetOfLists/SetOfDicts (reference equality for inner elements) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
… hashset comparers Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ish null from empty DictionaryHashCode and HashSetHashCode previously initialized hashCode to 0, causing null and empty collections to produce the same hash (0). This violates the expectation that null != empty in hash-based collections. Using 1 as the sentinel for empty matches the pattern already established in EqualityHelper. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
… comparers DictionaryEqualityComparer, ReadOnlyDictionaryEqualityComparer, and HashSetEqualityComparer all initialized GetHashCode to 0, causing null and empty collections to produce the same hash. This violates the Equals/GetHashCode contract: Equals correctly returns false for (null, empty), so GetHashCode must also differ to avoid unnecessary hash table collisions. Starting from 1 ensures empty collections are always distinguishable from null (which returns 0). Added comments explaining why the seed is 1 and what problem it solves. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…l hash check Rename all property-based test methods across four files to follow the Invariant_Subject_Condition pattern, making the tested invariant explicit in the name rather than describing the operation. Fix incorrect EqualImpliesSameHashCode property in ReadOnlyDictionaryComparerProperties and OrderedDictionaryComparerProperties: was asserting Equals(a,b) ↔ hash(a)==hash(b) (bidirectional), which fails on hash collisions. Corrected to one-directional Prop.When(Equals(a,b), hash(a)==hash(b)), which is the actual contract. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…o EquatableGenerator - Add InferCollectionComparer public hook: infers structural collection comparers (DictionaryEqualityComparer, SequenceEqualityComparer, ReadOnlyDictionaryEqualityComparer) for adapter generators so [Key] / [DataMember] properties don't require explicit equality attributes. Recurses into nested types via BuildElementComparerExpression. - Add IsEquatableGeneratorAttribute: recognises *EquatableAttribute across all three adapter namespaces (Equatable.Attributes, .DataContract, .MessagePack) so derived classes correctly delegate to base.Equals() regardless of which adapter annotates the base. - Wire InferCollectionComparer into DataContractEquatableGenerator and MessagePackEquatableGenerator as postProcessProperty delegate. - Update DataContractEquatableAnalyzer and MessagePackEquatableAnalyzer to use IsEquatableGeneratorAttribute for base-class detection. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…agePack Add OrderDataContractNested and SerializedRecordNested — partial classes annotated with [DataContractEquatable] / [MessagePackEquatable] carrying nested collection properties (Dict<K,List<V>>, Dict<K,Dict<K2,V>>, List<Dict<K,V>>, IReadOnlyDictionary<K,List<V>>) used by entity-level integration tests. Update existing OrderDataContract and SerializedRecord entities as needed. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ctions, multi-dim arrays - ComparerGetHashCodeTest: covers null→0, empty→non-zero, empty≠null, and equal-implies-same-hash for all five comparers (Dictionary, ReadOnlyDictionary, HashSet, Sequence, OrderedDictionary). - MultiDimensionalArrayEqualityComparerTest: full coverage of MultiDimensionalArrayEqualityComparer — rank checks, dimension checks, 2D/3D content equality, custom comparer, GetHashCode row-major order sensitivity. - NestedDictionaryEqualityComparerTest: explicitly-composed nested comparers (Dict<K,Dict>, Dict<K,List>, Dict<K,HashSet>, 3-level, ReadOnlyDict<K,List>). - Add custom-comparer constructor path tests (Path C) to HashSetEqualityComparerTest and SequenceEqualityComparerTest. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…erage - Extract shared AnalyzerTestHelper from EquatableAnalyzerTest. - Add DataContractAnalyzerTest: EQ0020 (missing [DataContract]) and derived-class checks. - Add MessagePackAnalyzerTest: EQ0021 (missing [MessagePackObject]) and derived-class checks. - Add 7 tests to EquatableAnalyzerTest covering ISet<T>, IReadOnlySet<T>, int[], int[,] missing-attribute diagnostics (EQ0002) and valid [HashSetEquality]/[SequenceEquality] on those types. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…r coverage - AdapterGeneratorTestBase: shared base class with BuildReferences and GetGeneratedOutput helpers for DataContract and MessagePack generator tests. - DataContractGeneratorTest: 7 snapshot tests covering basic generation, derived-from-annotated-base delegation, unannotated-base inclusion, explicit comparer override, inferred collection comparers, nested collection comparers, and zero-property edge case. - MessagePackGeneratorTest: same 7 scenarios for MessagePack. - EquatableGeneratorTest: add GenerateEquatableWithNoPublicProperties snapshot (Equals reduces to !(other is null)). - EquatableWriterTest: add 4 snapshot tests for record, sealed, derived, and nested-class writer paths previously uncovered. - Accept all new verified snapshots. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ed entities - OrderDataContractNestedTest: 13 tests verifying [DataContractEquatable]-generated Equals/GetHashCode on Dict<K,List<V>>, Dict<K,Dict<K2,V>>, List<Dict<K,V>>, IReadOnlyDictionary<K,List<V>> — structural inner comparison, insertion-order independence for dicts, order-sensitivity for lists, null vs empty discrimination. - SerializedRecordNestedTest: same 13 scenarios for [MessagePackEquatable]. - DictionaryHashCodeTest: documents and verifies order-independent hash algorithm. - Add OrderDataContractProperties and SerializedRecordProperties to the property test project, and update project file accordingly. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
All 7 MessagePack test scenarios now use identical entity class names, property names, and property types as their DataContract counterparts (OrderDataContract, DerivedContract/BaseContract, ConcreteRecord/UnannotatedBase, OrderedContract, ContractWithCollections, ContractWithNestedCollections, AllIgnored). Only the serialization attributes differ: [Key(n)]/[IgnoreMember]/[MessagePackObject]/ [MessagePackEquatable] replace [DataMember(Order=n)]/[IgnoreDataMember]/[DataContract]/ [DataContractEquatable]. Snapshots regenerated and accepted. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
When [DictionaryEquality(sequential:true)] is applied, OrderedDictionaryEqualityComparer now propagates to all nested dictionary levels rather than only the outermost one. Adds nested-comparer tests to OrderedDictionaryEqualityComparerTest and snapshot tests verifying the generated output for Dictionary<K, Dictionary<K2, V>>. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
… on HashSet<T> Fixes two missing cases for explicit comparer overrides that reverse natural order-sensitivity: 1. [HashSetEquality] on T[] (array): ValidateComparer now accepts rank-1 arrays for HashSet kind; new BuildHashSetArrayComparerExpression emits HashSetEqualityComparer instead of SequenceEqualityComparer. 2. [SequenceEquality] on HashSet<T>: already validated and emitted correctly (no code change needed); documented with explicit snapshot tests. Adds 6 snapshot tests (base + DataContract + MessagePack × 2 directions). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…d collection levels
When [HashSetEquality] or [SequenceEquality] is explicitly annotated on a property,
the chosen comparer class now propagates into every nested enumerable/array level rather
than only the outermost. Similarly, [DictionaryEquality(sequential:true)] already
propagated dictKind to nested dicts; tests now cover 3-level nesting, mixed dict+list,
and the unordered case symmetrically with the enumKind tests.
- EquatableGenerator: thread enumKind through all nested collection builder calls;
remove IsEnumKindOverride guard so all explicit annotations always propagate
- ValidateComparer: accept IArrayTypeSymbol{Rank:1} as valid target for [HashSetEquality]
- BuildHashSetArrayComparerExpression: new helper for [HashSetEquality] on T[]
- NestedCollectionsProperties: rename ListOfSets_InnerOrderDoesNotMatter →
ListOfSets_InnerOrderMatters now that [SequenceEquality] propagates into inner sets
- New snapshot tests: GenerateHashSetEqualityPropagatesIntoNestedCollections,
GenerateSequenceEqualityPropagatesIntoNestedCollections,
GenerateDictionaryEqualityPropagatesIntoNestedDictionaries (+ DataContract/MessagePack variants)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Assert a.Equals(b) == b.Equals(a) across all nested collection shapes (Dict/List/HashSet/Array combinations) in both property test projects. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ection overrides Documents DataContractEquatable/MessagePackEquatable adapters, nested collection comparer propagation (single annotation propagates all levels), direction override examples (HashSetEquality on List, SequenceEquality on HashSet), and custom comparer pattern with clear table format. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…xamples Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…licit override explanation Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
… getting started Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…nal array Multi-dimensional arrays (rank ≥ 2) always use MultiDimensionalArrayEqualityComparer regardless of any annotation. Any collection or equality attribute on such a property has no effect (or worse, [EqualityComparer] silently bypasses the multi-dim comparer and produces reference equality). EQ0014 turns this silent wrong behavior into a visible compile-time warning. Changes: - DiagnosticDescriptors.cs: add EQ0014 descriptor - EquatableAnalyzer.cs: register EQ0014 in SupportedDiagnostics; skip EQ0002 for multi-dim arrays (they have a default); fire EQ0014 for any collection/equality attribute on rank ≥ 2 arrays - EquatableAnalyzerTest.cs: fix existing test (int[,] with no attribute → no warning); add 6 new tests covering EQ0014 for SequenceEquality, HashSetEquality, EqualityComparer, ReferenceEquality, 3D arrays, and the no-attribute baseline - README.md: add build-time diagnostics section documenting all EQ00xx codes with special explanation of EQ0014 and why override is impossible on multi-dim arrays Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…sed on a dictionary type Applying an enumerable attribute to a Dictionary<K,V> treats it as a flat sequence of KeyValuePair entries, discarding key-lookup semantics. This is almost always wrong. EQ0015 fires for [SequenceEquality] and [HashSetEquality] on any type that implements IDictionary<K,V> or IReadOnlyDictionary<K,V>; [DictionaryEquality] is the correct choice. Changes: - DiagnosticDescriptors.cs: add EQ0015 descriptor - EquatableAnalyzer.cs: register EQ0015; detect SequenceEquality/HashSetEquality on dictionary types and report EQ0015 - EquatableAnalyzerTest.cs: 5 new tests covering Dictionary<K,V>, IDictionary<K,V>, IReadOnlyDictionary<K,V>, and the valid [DictionaryEquality] baseline - README.md: add EQ0015 to the diagnostics table and add an explanation section Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…mple Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ullet list Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ages too Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…able types Adapter generators only include properties that carry the serialisation inclusion attribute ([DataMember] / [Key(n)]). All other public properties are silently skipped. Accidental omissions are easy to miss, so EQ0022/EQ0023 force the intent to be explicit: either add the inclusion attribute or suppress with [IgnoreDataMember] / [IgnoreMember] / [IgnoreEquality]. Also clarified in README: - EQ0001/EQ0002 apply to [Equatable] only; adapters auto-infer comparers - EQ0022/EQ0023 explanation with code example showing both paths to suppress Changes: - DataContractEquatableAnalyzer.cs: add EQ0022, check every public property - MessagePackEquatableAnalyzer.cs: add EQ0023, same pattern - DataContractAnalyzerTest.cs: 4 new tests for EQ0022 (fires, IgnoreDataMember, IgnoreEquality, multiple) - MessagePackAnalyzerTest.cs: 4 new tests for EQ0023 (fires, IgnoreMember, IgnoreEquality, multiple) - README.md: update diagnostics table and add explanations for all groups Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…r inference, overrides - Document which attributes include/exclude properties per adapter (table) - Explain EQ0022/EQ0023 in context of silent exclusion - Show that collection comparers are inferred — no [SequenceEquality] etc. needed - Show explicit attribute overrides still work on adapter-included properties - Fix package table: [DataContractEquatable] uses System.Runtime.Serialization, not WCF/protobuf-net specifically Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ntract, MessagePack Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…distinction with EQ022x reference Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…es, and improvements Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
… declarative approach; add struct support Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…y with Key(n) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ctions still use reference equality Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…nts IEquatable, not by special treatment Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ary docs Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Author
|
@pwelter34, @loresoft — PR with adapter generators, nested collection propagation, multi-dimensional array support, and new analyzer diagnostics (EQ0014/0015/0022/0023). Would appreciate a review when you have time. |
…adapter generators Properties annotated with both a serialisation attribute ([DataMember] or [Key(n)]) and [IgnoreEquality] are now correctly excluded from generated Equals / GetHashCode. Previously IsIncludedDataContract and IsIncludedMessagePack only checked the serialisation-specific exclusion attributes; the [IgnoreEquality] check was missing from both adapters. Adds snapshot tests proving the fix and a README section documenting the [DataMember(Order=n)][IgnoreEquality] pattern with a DataContract and a MessagePack example. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ashing The inline OrderedDictionaryHashCode helper in EquatableWriter iterated entries without OrderBy, while OrderedDictionaryEquals sorted by key. An equal pair with different insertion order would produce the same Equals result but different hash codes — a direct hash contract violation. Also adds missing multi-entry swapped-values tests for DictionaryEqualityComparer: - SwappedValues_UnequalDictionaries_EqualIsFalse — contract test (always passes) - SwappedValues_ProduceDifferentHashInPractice — regression guard for systematic collisions in the commutative sum hash Note: the inline helper is currently dead code (the generator always emits an explicit OrderedReadOnlyDictionaryEqualityComparer expression for ordered dicts). The fix prevents confusion and is correct if the path is ever reached. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…Writer OrderedDictionaryEquals and OrderedDictionaryHashCode were never reachable: the generator always emits an explicit OrderedReadOnlyDictionaryEqualityComparer expression for ordered-dictionary properties, so ComparerTypes.OrderedDictionary is never left on the property model when the writer is called. Dead code removed; no behaviour change. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
… different algorithm Explains that [DictionaryEquality(sequential: true)] produces the same equality result as plain [DictionaryEquality] for all inputs. The O(n log n) cost is only justified when a custom keyComparer implements both IEqualityComparer<K> and IComparer<K> and you need deterministic sort order for snapshot testing. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…tial dictionary equality README: concrete example showing StringComparer.OrdinalIgnoreCase driving both key equality and sort order in OrderedDictionaryEqualityComparer, with a note that the dictionary's own internal comparer is not visible to generated code. Tests: four cases covering case-insensitive equality, insertion-order-independent hash with custom comparer, case-variant hash equivalence, and the contrast with the default ordinal comparer where case variants are distinct keys. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ot just case Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…'s internal comparer y.TryGetValue uses the dictionary's own internal comparer, not this.KeyComparer. When a custom keyComparer is supplied, GetHashCode already uses it — so two entries equal under KeyComparer produced the same hash but Equals returned false, a direct hash contract violation. Fix: build a temporary Dictionary keyed by KeyComparer, then TryGetValue against that. O(n) cost preserved. Same fix applied to ReadOnlyDictionaryEqualityComparer. Property tests added to DictionaryComparerProperties and ReadOnlyDictionaryComparerProperties to permanently guard the invariant: for any custom keyComparer, Equals(x,y) implies GetHashCode(x) == GetHashCode(y). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…mparer correctly Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ed to prod Drops OrderedDictionaryEqualityComparer, OrderedReadOnlyDictionaryEqualityComparer, the sequential: true parameter on DictionaryEqualityAttribute, all generator code paths that routed to ComparerTypes.OrderedDictionary, and all related tests, snapshot files, and README documentation. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What's new
New packages
(System.Runtime.Serialization). Only properties annotated with [DataMember] are included in equality;
[IgnoreDataMember] and unannotated properties are excluded. EQ0022 is emitted for any public property with no
annotation, requiring the intent to be made explicit.
included; [IgnoreMember] and unannotated properties are excluded. EQ0023 is emitted for unannotated properties.
New features
comparing, producing deterministic equality regardless of insertion order. Useful for snapshot testing and diagnostic
logging. Propagates into nested dictionary values.
on HashSet enforces order-sensitive comparison.
kind into all nested levels. Dictionary<K, Dictionary<K2,V>>, Dictionary<K, List>, and three-level nesting are all
handled with a single annotation.
automatically as the default. Checks rank, dimension lengths, and element values in row-major order.
Dictionary<K,V>.
equatable-generated type, including across adapter boundaries (e.g. [Equatable] derived from [DataContractEquatable]).
Bug fixes
satisfying the hash contract.
SetEquals-based Equals.
values, not only the outermost level.
Improvements
what the project requires.