Skip to content

Split ObservableCacheEx.cs into per-family partial classes#1095

Open
dwcullop wants to merge 9 commits into
reactivemarbles:mainfrom
dwcullop:maint/cache_ext_breakup
Open

Split ObservableCacheEx.cs into per-family partial classes#1095
dwcullop wants to merge 9 commits into
reactivemarbles:mainfrom
dwcullop:maint/cache_ext_breakup

Conversation

@dwcullop
Copy link
Copy Markdown
Member

@dwcullop dwcullop commented May 26, 2026

Splits ObservableCacheEx.cs (6,830+ lines, 100+ distinct operator names spanning 290 method bodies) into one partial-class file per operator name (overload set), following the convention already established by ObservableCacheEx.SortAndBind.cs and ObservableCacheEx.VirtualiseAndPage.cs.

This revision addresses the review feedback on the original family-grouping split:

  1. One file per operator name (per overload set), not per family. The earlier 19 family files (ObservableCacheEx.Edit.cs with 27 operator names, etc.) are replaced with 103 per-operator partial files. Finding AddOrUpdate.cs is now exactly that.
  2. ObservableCacheEx.cs restored as a bare partial carrying the canonical class XML doc. Every per-operator partial repeats the same canonical summary (Extensions for dynamic data.) so the documentation never diverges between files and SA1601 is satisfied. ObservableCacheEx.SortAndBind.cs and ObservableCacheEx.VirtualiseAndPage.cs were also updated for consistency.
  3. Private helpers placed after public members within their containing file. Each private helper lives in the file of the alphabetically-first operator that calls it.

This is still a pure file reorganisation. No code, no XML documentation, no comments, no preprocessor directives, and no constants were added, removed, or altered. The byte content of every method body is preserved (verified programmatically against the original). #if SUPPORTS_BINDINGLIST (around the BindingList Bind overloads) and #if SUPPORTS_ASYNC_DISPOSABLE (around AsyncDisposeMany) are reconstructed in their new homes.

New files (one per operator name)

103 new files, one per public operator overload set. Private helpers placed at the bottom of the file of their alphabetically-first public caller:

Private helper Placed in Other callers
Combine ObservableCacheEx.And.cs Except, Or, Xor
ForForced ObservableCacheEx.Transform.cs TransformSafe
AdaptSelector ObservableCacheEx.Group.cs GroupOnObservable
OnChangeAction ObservableCacheEx.OnItemAdded.cs OnItemRefreshed, OnItemRemoved, OnItemUpdated
TrueFor ObservableCacheEx.TrueForAll.cs TrueForAny
CreateChangeSetTransformer ObservableCacheEx.TransformManyAsync.cs TransformManySafeAsync
DefaultResortOnSourceRefresh const ObservableCacheEx.MergeManyChangeSets.cs (only MergeManyChangeSets)
DefaultSortResetThreshold const ObservableCacheEx.Sort.cs (only Sort)

Files

File Operator Overloads
ObservableCacheEx.cs (bare partial, class XML doc only) -
ObservableCacheEx.Adapt.cs Adapt 2
ObservableCacheEx.AddOrUpdate.cs AddOrUpdate 5
ObservableCacheEx.And.cs And (+ Combine private) 5
ObservableCacheEx.AsObservableCache.cs AsObservableCache 2
ObservableCacheEx.AsyncDisposeMany.cs AsyncDisposeMany 1
ObservableCacheEx.AutoRefresh.cs AutoRefresh 2
ObservableCacheEx.AutoRefreshOnObservable.cs AutoRefreshOnObservable 2
ObservableCacheEx.Batch.cs Batch 1
ObservableCacheEx.BatchIf.cs BatchIf 5
ObservableCacheEx.Bind.cs Bind 12
ObservableCacheEx.BufferInitial.cs BufferInitial 1
ObservableCacheEx.Cast.cs Cast 1
ObservableCacheEx.ChangeKey.cs ChangeKey 2
ObservableCacheEx.Clear.cs Clear 3
ObservableCacheEx.Clone.cs Clone 1
ObservableCacheEx.Convert.cs Convert 1
ObservableCacheEx.DeferUntilLoaded.cs DeferUntilLoaded 2
ObservableCacheEx.DisposeMany.cs DisposeMany 1
ObservableCacheEx.DistinctValues.cs DistinctValues 1
ObservableCacheEx.EditDiff.cs EditDiff 4
ObservableCacheEx.EnsureUniqueKeys.cs EnsureUniqueKeys 1
ObservableCacheEx.Except.cs Except 5
ObservableCacheEx.ExpireAfter.cs ExpireAfter 5
ObservableCacheEx.Filter.cs Filter 4
ObservableCacheEx.FilterImmutable.cs FilterImmutable 1
ObservableCacheEx.FilterOnObservable.cs FilterOnObservable 2
ObservableCacheEx.FinallySafe.cs FinallySafe 1
ObservableCacheEx.Flatten.cs Flatten 1
ObservableCacheEx.FlattenBufferResult.cs FlattenBufferResult 1
ObservableCacheEx.ForEachChange.cs ForEachChange 1
ObservableCacheEx.FullJoin.cs FullJoin 2
ObservableCacheEx.FullJoinMany.cs FullJoinMany 2
ObservableCacheEx.Group.cs Group (+ AdaptSelector private) 5
ObservableCacheEx.GroupOnObservable.cs GroupOnObservable 2
ObservableCacheEx.GroupOnProperty.cs GroupOnProperty 1
ObservableCacheEx.GroupOnPropertyWithImmutableState.cs GroupOnPropertyWithImmutableState 1
ObservableCacheEx.GroupWithImmutableState.cs GroupWithImmutableState 1
ObservableCacheEx.IgnoreSameReferenceUpdate.cs IgnoreSameReferenceUpdate 1
ObservableCacheEx.IgnoreUpdateWhen.cs IgnoreUpdateWhen 1
ObservableCacheEx.IncludeUpdateWhen.cs IncludeUpdateWhen 1
ObservableCacheEx.InnerJoin.cs InnerJoin 2
ObservableCacheEx.InnerJoinMany.cs InnerJoinMany 2
ObservableCacheEx.InvokeEvaluate.cs InvokeEvaluate 1
ObservableCacheEx.LeftJoin.cs LeftJoin 2
ObservableCacheEx.LeftJoinMany.cs LeftJoinMany 2
ObservableCacheEx.LimitSizeTo.cs LimitSizeTo 2
ObservableCacheEx.MergeChangeSets.cs MergeChangeSets 16
ObservableCacheEx.MergeMany.cs MergeMany 2
ObservableCacheEx.MergeManyChangeSets.cs MergeManyChangeSets (+ DefaultResortOnSourceRefresh const) 14
ObservableCacheEx.MergeManyItems.cs MergeManyItems 2
ObservableCacheEx.MonitorStatus.cs MonitorStatus 1
ObservableCacheEx.NotEmpty.cs NotEmpty 1
ObservableCacheEx.OfType.cs OfType 1
ObservableCacheEx.OnItemAdded.cs OnItemAdded (+ OnChangeAction private) 2
ObservableCacheEx.OnItemRefreshed.cs OnItemRefreshed 2
ObservableCacheEx.OnItemRemoved.cs OnItemRemoved 2
ObservableCacheEx.OnItemUpdated.cs OnItemUpdated 2
ObservableCacheEx.Or.cs Or 5
ObservableCacheEx.PopulateFrom.cs PopulateFrom 2
ObservableCacheEx.PopulateInto.cs PopulateInto 3
ObservableCacheEx.QueryWhenChanged.cs QueryWhenChanged 3
ObservableCacheEx.RefCount.cs RefCount 1
ObservableCacheEx.Refresh.cs Refresh 3
ObservableCacheEx.Remove.cs Remove 6
ObservableCacheEx.RemoveKey.cs RemoveKey 2
ObservableCacheEx.RemoveKeys.cs RemoveKeys 1
ObservableCacheEx.RightJoin.cs RightJoin 2
ObservableCacheEx.RightJoinMany.cs RightJoinMany 2
ObservableCacheEx.SkipInitial.cs SkipInitial 1
ObservableCacheEx.Sort.cs Sort (+ DefaultSortResetThreshold const) 4
ObservableCacheEx.SortBy.cs SortBy 1
ObservableCacheEx.StartWithEmpty.cs StartWithEmpty 7
ObservableCacheEx.StartWithItem.cs StartWithItem 2
ObservableCacheEx.SubscribeMany.cs SubscribeMany 2
ObservableCacheEx.SuppressRefresh.cs SuppressRefresh 1
ObservableCacheEx.Switch.cs Switch 2
ObservableCacheEx.ToCollection.cs ToCollection 1
ObservableCacheEx.ToObservableChangeSet.cs ToObservableChangeSet 2
ObservableCacheEx.ToObservableOptional.cs ToObservableOptional 2
ObservableCacheEx.ToSortedCollection.cs ToSortedCollection 2
ObservableCacheEx.Transform.cs Transform (+ ForForced private) 9
ObservableCacheEx.TransformAsync.cs TransformAsync 6
ObservableCacheEx.TransformImmutable.cs TransformImmutable 1
ObservableCacheEx.TransformMany.cs TransformMany 4
ObservableCacheEx.TransformManyAsync.cs TransformManyAsync (+ CreateChangeSetTransformer private) 6
ObservableCacheEx.TransformManySafeAsync.cs TransformManySafeAsync 6
ObservableCacheEx.TransformOnObservable.cs TransformOnObservable 2
ObservableCacheEx.TransformSafe.cs TransformSafe 6
ObservableCacheEx.TransformSafeAsync.cs TransformSafeAsync 6
ObservableCacheEx.TransformToTree.cs TransformToTree 1
ObservableCacheEx.TransformWithInlineUpdate.cs TransformWithInlineUpdate 4
ObservableCacheEx.TreatMovesAsRemoveAdd.cs TreatMovesAsRemoveAdd 1
ObservableCacheEx.TrueForAll.cs TrueForAll (+ TrueFor private) 2
ObservableCacheEx.TrueForAny.cs TrueForAny 2
ObservableCacheEx.UpdateIndex.cs UpdateIndex 1
ObservableCacheEx.Watch.cs Watch 1
ObservableCacheEx.WatchValue.cs WatchValue 2
ObservableCacheEx.WhenAnyPropertyChanged.cs WhenAnyPropertyChanged 1
ObservableCacheEx.WhenPropertyChanged.cs WhenPropertyChanged 1
ObservableCacheEx.WhenValueChanged.cs WhenValueChanged 1
ObservableCacheEx.WhereReasonsAre.cs WhereReasonsAre 1
ObservableCacheEx.WhereReasonsAreNot.cs WhereReasonsAreNot 1
ObservableCacheEx.Xor.cs Xor 5

Touched pre-existing files

  • ObservableCacheEx.SortAndBind.cs and ObservableCacheEx.VirtualiseAndPage.cs had their class-level XML summaries replaced with the canonical Extensions for dynamic data. text so all partials carry consistent documentation. No code in those files was changed.

Verification

The split was generated programmatically with byte-level per-method equality checks against the original. Every public method and every private helper used by the public surface was confirmed to be present in exactly one file. The full test suite passes (one pre-existing flaky concurrency test in SuspendNotificationsFixture is unrelated and fails on main too).

dwcullop added 5 commits May 25, 2026 22:59
Splits the 6800-line ObservableCacheEx.cs into 24 smaller partial-class files grouped by operator family. Each method (and all of its overloads) lives in exactly one file. No code, comments, or XML documentation is added, removed, or otherwise modified; this is a pure file reorganization. All 2218 tests pass.
Splits the monolithic ObservableCacheEx.cs into 19 smaller partial-class files grouped by operator family. The two pre-existing partials (ObservableCacheEx.SortAndBind.cs, ObservableCacheEx.VirtualiseAndPage.cs) are untouched. Each method (and all of its overloads) lives in exactly one file. No code, XML documentation, comments, preprocessor directives, or constants are added, removed, or otherwise modified. The split was generated programmatically with byte-level per-method equality checks against the original.
Sorts members alphabetically by name within each new partial file. Overloads of the same name preserve their original declaration order. Constants sort before methods. Pre-existing partials (SortAndBind, VirtualiseAndPage) are not modified.
@dwcullop dwcullop enabled auto-merge (squash) May 26, 2026 14:37
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

JakenVeina
JakenVeina previously approved these changes May 30, 2026
Comment thread src/DynamicData/Cache/ObservableCacheEx.Adapt.cs Outdated
Comment thread src/DynamicData/Cache/ObservableCacheEx.Edit.cs Outdated
return sources.Combine(CombineOperator.And);
}

private static IObservable<IChangeSet<TObject, TKey>> Combine<TObject, TKey>(this IObservableList<IObservableCache<TObject, TKey>> source, CombineOperator type)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For future reference: private operators like these are definitely the kinda thing we should pull into whatever the new "internal operators" system ends up being.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not internal. It can only be used within this class.

Comment thread src/DynamicData/Cache/ObservableCacheEx.Query.cs Outdated
@JakenVeina JakenVeina self-requested a review May 30, 2026 02:43
@JakenVeina JakenVeina dismissed their stale review May 30, 2026 02:43

Goddamnit, GitHub.

Copy link
Copy Markdown
Collaborator

@JakenVeina JakenVeina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seriously, goddamnit, GitHub.

dwcullop added 2 commits May 30, 2026 10:26
…oad set)

Addresses PR review feedback:

1. ONE FILE PER OPERATOR NAME (one overload set per file). The previous split
   into 19 family files is replaced with 103 per-operator partial files,
   matching the existing convention set by ObservableCacheEx.SortAndBind.cs
   and ObservableCacheEx.VirtualiseAndPage.cs.

2. BARE ObservableCacheEx.cs FILE restored to carry the canonical class-level
   XML documentation. All partials carry the same canonical class summary
   ('Extensions for dynamic data.') so SA1601 is satisfied and there are no
   divergent per-file class docs. SortAndBind.cs and VirtualiseAndPage.cs
   were also updated for consistency.

3. PRIVATE HELPERS placed AFTER all public members within their containing
   file. Each private helper lives in the alphabetically-first operator file
   that calls it:
     - Combine -> And.cs (also called by Except, Or, Xor)
     - ForForced -> Transform.cs (also called by TransformSafe)
     - AdaptSelector -> Group.cs (also called by GroupOnObservable)
     - OnChangeAction -> OnItemAdded.cs (also called by OnItem* family)
     - TrueFor -> TrueForAll.cs (also called by TrueForAny)
     - CreateChangeSetTransformer -> TransformManyAsync.cs (also called by TransformManySafeAsync)
     - DefaultResortOnSourceRefresh const -> MergeManyChangeSets.cs
     - DefaultSortResetThreshold const -> Sort.cs

The byte content of every method body is preserved (verified programmatically).
#if/#endif preprocessor regions (SUPPORTS_BINDINGLIST in Bind.cs,
SUPPORTS_ASYNC_DISPOSABLE around AsyncDisposeMany) are reconstructed in the
new files.
@dwcullop dwcullop requested a review from JakenVeina May 30, 2026 18:10
dwcullop added a commit to dwcullop/DynamicData that referenced this pull request May 30, 2026
…ad set)

Mirrors the same convention applied to ObservableCacheEx (PR reactivemarbles#1095):

1. ONE FILE PER OPERATOR NAME (one overload set per file). The previous 17
   family files are replaced with 63 per-operator partial files.

2. BARE ObservableListEx.cs FILE restored to carry the canonical class-level
   XML documentation. All partials carry the same canonical class summary
   ('Extensions for ObservableList.') so SA1601 is satisfied and there are
   no divergent per-file class docs.

3. PRIVATE HELPERS placed AFTER all public members within their containing
   file. The 5 private 'Combine' overloads (used by And, Except, Or, Xor)
   are placed at the bottom of And.cs (alphabetically first caller).

The byte content of every method body is preserved (verified programmatically).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants