Split ObservableCacheEx.cs into per-family partial classes#1095
Split ObservableCacheEx.cs into per-family partial classes#1095dwcullop wants to merge 9 commits into
Conversation
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.
…to rebased split
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.
| return sources.Combine(CombineOperator.And); | ||
| } | ||
|
|
||
| private static IObservable<IChangeSet<TObject, TKey>> Combine<TObject, TKey>(this IObservableList<IObservableCache<TObject, TKey>> source, CombineOperator type) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
It's not internal. It can only be used within this class.
There was a problem hiding this comment.
Right.... but it's the same concept. It's an operator that only exists internally, as part of the implementation of other operators.
JakenVeina
left a comment
There was a problem hiding this comment.
Seriously, goddamnit, GitHub.
…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.
…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).
| return sources.Combine(CombineOperator.And); | ||
| } | ||
|
|
||
| private static IObservable<IChangeSet<TObject, TKey>> Combine<TObject, TKey>(this IObservableList<IObservableCache<TObject, TKey>> source, CombineOperator type) |
There was a problem hiding this comment.
I would say .Combine() should get the same treatment, since it's used by more than just .And().
| /// </summary> | ||
| public static partial class ObservableCacheEx | ||
| { | ||
| #if SUPPORTS_ASYNC_DISPOSABLE |
There was a problem hiding this comment.
Since this is now at the scope of basically the whole file, it might be better to include/exclude the whole file at the project level. I feel like that's how we're doing it elsewhere in the codebase? I might be confusing with another project, though. Something to circle back to, maybe, if it's cleaner.
| } | ||
|
|
||
| // TODO: Apply the Adapter to more places | ||
| private static Func<TObject, TKey, TResult> AdaptSelector<TObject, TKey, TResult>(Func<TObject, TResult> other) |
There was a problem hiding this comment.
This also should probably get its own file, since it's used by multiple different operators.
| where TKey : notnull | ||
| => source.OnItemAdded((obj, _) => addAction(obj)); | ||
|
|
||
| private static IObservable<IChangeSet<TObject, TKey>> OnChangeAction<TObject, TKey>(this IObservable<IChangeSet<TObject, TKey>> source, Predicate<Change<TObject, TKey>> predicate, Action<Change<TObject, TKey>> changeAction) |
There was a problem hiding this comment.
Same deal as above, probably deserves it's own file.
| return new Sort<TObject, TKey>(source, comparer, sortOptimisations, null, resorter, resetThreshold).Run(); | ||
| } | ||
|
|
||
| private const int DefaultSortResetThreshold = 100; |
There was a problem hiding this comment.
Teeeeeeeechnically, I could say the same about this guy, it's used by multiple operators, so it should go in a shared file (I would just put it in the "core" file, ObservableCacheEx.cs), but like... am I being too pedantic?
| return source.Transform(transformFactory, forceTransform.ForForced<TSource, TKey>()); | ||
| } | ||
|
|
||
| private static IObservable<Func<TSource, TKey, bool>>? ForForced<TSource, TKey>(this IObservable<Unit>? source) |
| where TSource : notnull | ||
| where TSourceKey : notnull => source.TransformManyAsync((val, _) => manySelector(val), equalityComparer, comparer); | ||
|
|
||
| private static Func<TSource, TSourceKey, Task<IObservable<IChangeSet<TDestination, TDestinationKey>>>> CreateChangeSetTransformer<TDestination, TDestinationKey, TSource, TSourceKey>(Func<TSource, TSourceKey, Task<IEnumerable<TDestination>>> manySelector, Func<TDestination, TDestinationKey> keySelector) |
| where TKey : notnull | ||
| where TValue : notnull => source.TrueFor(observableSelector, items => items.All(o => o.LatestValue.HasValue && equalityCondition(o.Item, o.LatestValue.Value))); | ||
|
|
||
| private static IObservable<bool> TrueFor<TObject, TKey, TValue>(this IObservable<IChangeSet<TObject, TKey>> source, Func<TObject, IObservable<TValue>> observableSelector, Func<IEnumerable<ObservableWithValue<TObject, TValue>>, bool> collectionMatcher) |
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 byObservableCacheEx.SortAndBind.csandObservableCacheEx.VirtualiseAndPage.cs.This revision addresses the review feedback on the original family-grouping split:
ObservableCacheEx.Edit.cswith 27 operator names, etc.) are replaced with 103 per-operator partial files. FindingAddOrUpdate.csis now exactly that.ObservableCacheEx.csrestored 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.csandObservableCacheEx.VirtualiseAndPage.cswere also updated for consistency.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 BindingListBindoverloads) and#if SUPPORTS_ASYNC_DISPOSABLE(aroundAsyncDisposeMany) 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:
CombineObservableCacheEx.And.csExcept,Or,XorForForcedObservableCacheEx.Transform.csTransformSafeAdaptSelectorObservableCacheEx.Group.csGroupOnObservableOnChangeActionObservableCacheEx.OnItemAdded.csOnItemRefreshed,OnItemRemoved,OnItemUpdatedTrueForObservableCacheEx.TrueForAll.csTrueForAnyCreateChangeSetTransformerObservableCacheEx.TransformManyAsync.csTransformManySafeAsyncDefaultResortOnSourceRefreshconstObservableCacheEx.MergeManyChangeSets.csMergeManyChangeSets)DefaultSortResetThresholdconstObservableCacheEx.Sort.csSort)Files
Combineprivate)AdaptSelectorprivate)DefaultResortOnSourceRefreshconst)OnChangeActionprivate)DefaultSortResetThresholdconst)ForForcedprivate)CreateChangeSetTransformerprivate)TrueForprivate)Touched pre-existing files
ObservableCacheEx.SortAndBind.csandObservableCacheEx.VirtualiseAndPage.cshad their class-level XML summaries replaced with the canonicalExtensions 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
SuspendNotificationsFixtureis unrelated and fails onmaintoo).