diff --git a/src/DynamicData/Cache/ObservableCacheEx.SortAndBind.cs b/src/DynamicData/Cache/ObservableCacheEx.SortAndBind.cs index 06c14e0d..d7194c5b 100644 --- a/src/DynamicData/Cache/ObservableCacheEx.SortAndBind.cs +++ b/src/DynamicData/Cache/ObservableCacheEx.SortAndBind.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2025 Roland Pheasant. All rights reserved. +// Copyright (c) 2011-2025 Roland Pheasant. All rights reserved. // Roland Pheasant licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -18,8 +18,9 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The resulting read only observable collection. + /// The source of . + /// The resulting read only observable collection. + /// /// An observable which will emit change sets. public static IObservable> Bind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable>> source, @@ -38,9 +39,10 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The resulting read only observable collection. - /// Bind and sort default options. + /// The source of . + /// The resulting read only observable collection. + /// The Bind and sort default options. + /// /// An observable which will emit change sets. public static IObservable> Bind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable>> source, @@ -60,8 +62,9 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The list to bind to. + /// The source of . + /// The list to bind to. + /// /// An observable which will emit change sets. public static IObservable> Bind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable>> source, @@ -75,9 +78,12 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The list to bind to. - /// Bind and sort default options. + /// The source of . + /// The list to bind to. + /// The Bind and sort default options. + /// + /// + /// /// An observable which will emit change sets. public static IObservable> Bind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable>> source, @@ -92,8 +98,9 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The resulting read only observable collection. + /// The source of . + /// The resulting read only observable collection. + /// /// An observable which will emit change sets. public static IObservable> Bind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable>> source, @@ -112,9 +119,10 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The resulting read only observable collection. - /// Bind and sort default options. + /// The source of . + /// The resulting read only observable collection. + /// The Bind and sort default options. + /// /// An observable which will emit change sets. public static IObservable> Bind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable>> source, @@ -134,8 +142,9 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The list to bind to. + /// The source of . + /// The list to bind to. + /// /// An observable which will emit change sets. public static IObservable> Bind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable>> source, @@ -149,9 +158,12 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The list to bind to. - /// Bind and sort default options. + /// The source of . + /// The list to bind to. + /// The Bind and sort default options. + /// + /// + /// /// An observable which will emit change sets. public static IObservable> Bind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable>> source, @@ -166,8 +178,9 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The list to bind to. + /// The source of . + /// The list to bind to. + /// /// An observable which will emit change sets. public static IObservable> SortAndBind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable> source, @@ -181,9 +194,10 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The list to bind to. - /// Bind and sort default options. + /// The source of . + /// The list to bind to. + /// The Bind and sort default options. + /// /// An observable which will emit change sets. public static IObservable> SortAndBind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable> source, @@ -198,9 +212,10 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The list to bind to. - /// The comparer to order the resulting dataset. + /// The source of . + /// The list to bind to. + /// An comparer to order the resulting dataset. + /// /// An observable which will emit change sets. public static IObservable> SortAndBind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable> source, @@ -215,10 +230,21 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The list to bind to. - /// The comparer to order the resulting dataset. - /// Bind and sort default options. + /// The source of . + /// The list to bind to. + /// An comparer to order the resulting dataset. + /// The Bind and sort default options. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// /// An observable which will emit change sets. public static IObservable> SortAndBind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable> source, @@ -234,9 +260,10 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The list to bind to. - /// An observable of comparers which enables the sort order to be changed.> + /// The source of . + /// The list to bind to. + /// An of which enables the sort order to be changed.> + /// /// An observable which will emit change sets. public static IObservable> SortAndBind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable> source, @@ -251,10 +278,11 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The list to bind to. - /// An observable of comparers which enables the sort order to be changed.> - /// Bind and sort default options. + /// The source of . + /// The list to bind to. + /// An of which enables the sort order to be changed.> + /// The Bind and sort default options. + /// /// An observable which will emit change sets. public static IObservable> SortAndBind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable> source, @@ -270,8 +298,9 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The resulting read only observable collection. + /// The source of . + /// The resulting read only observable collection. + /// /// An observable which will emit change sets. public static IObservable> SortAndBind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable> source, @@ -285,9 +314,10 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The resulting read only observable collection. - /// Bind and sort default options. + /// The source of . + /// The resulting read only observable collection. + /// The Bind and sort default options. + /// /// An observable which will emit change sets. public static IObservable> SortAndBind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable> source, @@ -302,9 +332,10 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The resulting read only observable collection. - /// The comparer to order the resulting dataset. + /// The source of . + /// The resulting read only observable collection. + /// An comparer to order the resulting dataset. + /// /// An observable which will emit change sets. public static IObservable> SortAndBind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable> source, @@ -319,10 +350,11 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The resulting read only observable collection. - /// The comparer to order the resulting dataset. - /// Bind and sort default options. + /// The source of . + /// The resulting read only observable collection. + /// An comparer to order the resulting dataset. + /// The Bind and sort default options. + /// /// An observable which will emit change sets. public static IObservable> SortAndBind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable> source, @@ -347,9 +379,10 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The resulting read only observable collection. - /// An observable of comparers which enables the sort order to be changed. + /// The source of . + /// The resulting read only observable collection. + /// An of which enables the sort order to be changed. + /// /// An observable which will emit change sets. public static IObservable> SortAndBind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable> source, @@ -364,10 +397,11 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The resulting read only observable collection. - /// An observable of comparers which enables the sort order to be changed.> - /// Bind and sort default options. + /// The source of . + /// The resulting read only observable collection. + /// An of which enables the sort order to be changed.> + /// The Bind and sort default options. + /// /// An observable which will emit change sets. public static IObservable> SortAndBind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TObject, TKey>( this IObservable> source, diff --git a/src/DynamicData/Cache/ObservableCacheEx.VirtualiseAndPage.cs b/src/DynamicData/Cache/ObservableCacheEx.VirtualiseAndPage.cs index d4a5f060..b6b8e141 100644 --- a/src/DynamicData/Cache/ObservableCacheEx.VirtualiseAndPage.cs +++ b/src/DynamicData/Cache/ObservableCacheEx.VirtualiseAndPage.cs @@ -17,9 +17,10 @@ public static partial class ObservableCacheEx /// /// The type of the object. /// The type of the key. - /// The source. - /// The comparer to order the resulting dataset. - /// The virtualizing requests. + /// The source of . + /// An comparer to order the resulting dataset. + /// An of that specifies the virtualizing parameters. + /// /// An observable which will emit virtual change sets. /// source. public static IObservable>> SortAndVirtualize(this IObservable> source, @@ -34,9 +35,10 @@ public static IObservable>> So /// /// The type of the object. /// The type of the key. - /// The source. - /// An observable of comparers which enables the sort order to be changed.> - /// The virtualizing requests. + /// The source of . + /// An of which enables the sort order to be changed.> + /// An of that specifies the virtualizing parameters. + /// /// An observable which will emit virtual change sets. /// source. public static IObservable>> SortAndVirtualize( @@ -57,10 +59,11 @@ public static IObservable>> So /// /// The type of the object. /// The type of the key. - /// The source. - /// The comparer to order the resulting dataset. - /// The virtualizing requests. - /// Addition optimization options for virtualization. + /// The source of . + /// An comparer to order the resulting dataset. + /// An of that specifies the virtualizing parameters. + /// The Addition optimization options for virtualization. + /// /// An observable which will emit virtual change sets. /// source. public static IObservable>> SortAndVirtualize( @@ -82,10 +85,13 @@ public static IObservable>> So /// /// The type of the object. /// The type of the key. - /// The source. - /// An observable of comparers which enables the sort order to be changed.> - /// The virtualizing requests. - /// Addition optimization options for virtualization. + /// The source of . + /// An of which enables the sort order to be changed.> + /// An of that specifies the virtualizing parameters. + /// The Addition optimization options for virtualization. + /// + /// + /// /// An observable which will emit virtual change sets. /// source. public static IObservable>> SortAndVirtualize( @@ -107,8 +113,8 @@ public static IObservable>> So /// /// The type of the object. /// The type of the key. - /// The source. - /// The virtualising requests. + /// The source of . + /// An of that specifies the virtualizing parameters. /// An observable which will emit virtual change sets. /// source. [Obsolete(Constants.VirtualizeIsObsolete)] @@ -127,9 +133,10 @@ public static IObservable> Virtualise /// The type of the object. /// The type of the key. - /// The source. - /// The comparer. + /// The source of . + /// An comparer. /// The size. + /// /// An observable which will emit virtual change sets. /// source. /// size;Size should be greater than zero. @@ -153,8 +160,9 @@ public static IObservable>> To /// /// The type of the object. /// The type of the key. - /// The source. + /// The source of . /// The size. + /// /// An observable which will emit virtual change sets. /// source. /// size;Size should be greater than zero. @@ -178,9 +186,10 @@ public static IObservable> Top(t /// /// The type of the object. /// The type of the key. - /// The source. - /// The comparer to order the resulting dataset. - /// The virtualizing requests. + /// The source of . + /// An comparer to order the resulting dataset. + /// An of that specifies the paging parameters. + /// /// An observable which will emit virtual change sets. /// source. public static IObservable>> SortAndPage(this IObservable> source, @@ -195,9 +204,10 @@ public static IObservable>> SortA /// /// The type of the object. /// The type of the key. - /// The source. - /// An observable of comparers which enables the sort order to be changed.> - /// The virtualizing requests. + /// The source of . + /// An of which enables the sort order to be changed.> + /// An of that specifies the paging parameters. + /// /// An observable which will emit virtual change sets. /// source. public static IObservable>> SortAndPage( @@ -218,10 +228,11 @@ public static IObservable>> SortA /// /// The type of the object. /// The type of the key. - /// The source. - /// The comparer to order the resulting dataset. - /// The virtualizing requests. - /// Addition optimization options for virtualization. + /// The source of . + /// An comparer to order the resulting dataset. + /// An of that specifies the paging parameters. + /// The Addition optimization options for virtualization. + /// /// An observable which will emit virtual change sets. /// source. public static IObservable>> SortAndPage( @@ -243,10 +254,13 @@ public static IObservable>> SortA /// /// The type of the object. /// The type of the key. - /// The source. - /// An observable of comparers which enables the sort order to be changed.> - /// The virtualizing requests. - /// Addition optimization options for virtualization. + /// The source of . + /// An of which enables the sort order to be changed.> + /// An of that specifies the paging parameters. + /// The Addition optimization options for virtualization. + /// + /// + /// /// An observable which will emit virtual change sets. /// source. public static IObservable>> SortAndPage( @@ -268,8 +282,8 @@ public static IObservable>> SortA /// /// The type of the object. /// The type of the key. - /// The source. - /// The page requests. + /// The source of . + /// An of that specifies the paging parameters. /// An observable which emits change sets. [Obsolete(Constants.PageIsObsolete)] public static IObservable> Page(this IObservable> source, IObservable pageRequests) diff --git a/src/DynamicData/Cache/ObservableCacheEx.cs b/src/DynamicData/Cache/ObservableCacheEx.cs index 4edf3528..dda2dd62 100644 --- a/src/DynamicData/Cache/ObservableCacheEx.cs +++ b/src/DynamicData/Cache/ObservableCacheEx.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2025 Roland Pheasant. All rights reserved. +// Copyright (c) 2011-2025 Roland Pheasant. All rights reserved. // Roland Pheasant licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -81,6 +81,7 @@ public static IObservable> Adapt(this I /// The source. /// The item. /// source. + /// public static void AddOrUpdate(this ISourceCache source, TObject item) where TObject : notnull where TKey : notnull @@ -99,6 +100,7 @@ public static void AddOrUpdate(this ISourceCache s /// The item. /// The equality comparer used to determine whether a new item is the same as an existing cached item. /// source. + /// public static void AddOrUpdate(this ISourceCache source, TObject item, IEqualityComparer equalityComparer) where TObject : notnull where TKey : notnull @@ -118,6 +120,7 @@ public static void AddOrUpdate(this ISourceCache s /// The source. /// The items. /// source. + /// public static void AddOrUpdate(this ISourceCache source, IEnumerable items) where TObject : notnull where TKey : notnull @@ -138,6 +141,10 @@ public static void AddOrUpdate(this ISourceCache s /// The items. /// The equality comparer used to determine whether a new item is the same as an existing cached item. /// source. + /// + /// + /// + /// public static void AddOrUpdate(this ISourceCache source, IEnumerable items, IEqualityComparer equalityComparer) where TObject : notnull where TKey : notnull @@ -156,6 +163,7 @@ public static void AddOrUpdate(this ISourceCache s /// The item to add or update. /// The key to add or update. /// source. + /// public static void AddOrUpdate(this IIntermediateCache source, TObject item, TKey key) where TObject : notnull where TKey : notnull @@ -955,6 +963,8 @@ public static IObservable> ChangeKeyThe type of the key. /// The source. /// source. + /// + /// public static void Clear(this ISourceCache source) where TObject : notnull where TKey : notnull @@ -971,6 +981,7 @@ public static void Clear(this ISourceCache source) /// The type of the key. /// The source. /// source. + /// public static void Clear(this IIntermediateCache source) where TObject : notnull where TKey : notnull @@ -987,6 +998,7 @@ public static void Clear(this IIntermediateCache s /// The type of the key. /// The source. /// source. + /// public static void Clear(this LockFreeObservableCache source) where TObject : notnull where TKey : notnull @@ -1154,6 +1166,7 @@ public static IObservable> DistinctValuesThe items to add, update or delete. /// The equality comparer used to determine whether a new item is the same as an existing cached item. /// source. + /// public static void EditDiff(this ISourceCache source, IEnumerable allItems, IEqualityComparer equalityComparer) where TObject : notnull where TKey : notnull @@ -1175,6 +1188,9 @@ public static void EditDiff(this ISourceCache sour /// The items to compare and add, update or delete. /// Expression to determine whether an item's value is equal to the old value (current, previous) => current.Version == previous.Version. /// source. + /// + /// + /// public static void EditDiff(this ISourceCache source, IEnumerable allItems, Func areItemsEqual) where TObject : notnull where TKey : notnull @@ -1197,6 +1213,7 @@ public static void EditDiff(this ISourceCache sour /// Optional instance to use for comparing values. /// An observable cache. /// source. + /// public static IObservable> EditDiff(this IObservable> source, Func keySelector, IEqualityComparer? equalityComparer = null) where TObject : notnull where TKey : notnull @@ -1217,6 +1234,7 @@ public static IObservable> EditDiff(thi /// Optional instance to use for comparing values. /// An observable changeset. /// source. + /// public static IObservable> EditDiff(this IObservable> source, Func keySelector, IEqualityComparer? equalityComparer = null) where TObject : notnull where TKey : notnull @@ -2466,6 +2484,7 @@ public static IObservable>> LimitSizeTo< /// source /// or /// observableSelector. + /// public static IObservable MergeMany(this IObservable> source, Func> observableSelector) where TObject : notnull where TKey : notnull @@ -2489,6 +2508,7 @@ public static IObservable MergeMany(t /// source /// or /// observableSelector. + /// public static IObservable MergeMany(this IObservable> source, Func> observableSelector) where TObject : notnull where TKey : notnull @@ -3150,6 +3170,7 @@ public static IObservable> MergeManyCh /// Factory Function used to create child changesets. /// Optional instance to determine if two elements are the same. /// The result from merging the child changesets together. + /// public static IObservable> MergeManyChangeSets(this IObservable> source, Func>> observableSelector, IEqualityComparer? equalityComparer = null) where TObject : notnull where TKey : notnull @@ -3171,6 +3192,7 @@ public static IObservable> MergeManyChangeSetsFactory Function used to create child changesets. /// Optional instance to determine if two elements are the same. /// The result from merging the child changesets together. + /// public static IObservable> MergeManyChangeSets(this IObservable> source, Func>> observableSelector, IEqualityComparer? equalityComparer = null) where TObject : notnull where TKey : notnull @@ -3530,6 +3552,7 @@ public static IObservable> Or(this IObs /// or /// keySelector. /// + /// public static IDisposable PopulateFrom(this ISourceCache source, IObservable> observable) where TObject : notnull where TKey : notnull @@ -3552,6 +3575,7 @@ public static IDisposable PopulateFrom(this ISourceCache + /// public static IDisposable PopulateFrom(this ISourceCache source, IObservable observable) where TObject : notnull where TKey : notnull @@ -3574,6 +3598,8 @@ public static IDisposable PopulateFrom(this ISourceCache + /// + /// public static IDisposable PopulateInto(this IObservable> source, ISourceCache destination) where TObject : notnull where TKey : notnull @@ -3595,6 +3621,7 @@ public static IDisposable PopulateInto(this IObservablesource /// or /// destination. + /// public static IDisposable PopulateInto(this IObservable> source, IIntermediateCache destination) where TObject : notnull where TKey : notnull @@ -3613,6 +3640,7 @@ public static IDisposable PopulateInto(this IObservableThe source. /// The destination. /// A disposable which will unsubscribe from the source. + /// public static IDisposable PopulateInto(this IObservable> source, LockFreeObservableCache destination) where TObject : notnull where TKey : notnull @@ -3708,6 +3736,7 @@ public static IObservable> RefCount(thi /// The source. /// The item. /// source. + /// public static void Refresh(this ISourceCache source, TObject item) where TObject : notnull where TKey : notnull @@ -3725,6 +3754,8 @@ public static void Refresh(this ISourceCache sourc /// The source. /// The items. /// source. + /// + /// public static void Refresh(this ISourceCache source, IEnumerable items) where TObject : notnull where TKey : notnull @@ -3741,6 +3772,7 @@ public static void Refresh(this ISourceCache sourc /// The type of the key. /// The source. /// source. + /// public static void Refresh(this ISourceCache source) where TObject : notnull where TKey : notnull @@ -3759,6 +3791,7 @@ public static void Refresh(this ISourceCache sourc /// The source. /// The item. /// source. + /// public static void Remove(this ISourceCache source, TObject item) where TObject : notnull where TKey : notnull @@ -3777,6 +3810,7 @@ public static void Remove(this ISourceCache source /// The source. /// The key. /// source. + /// public static void Remove(this ISourceCache source, TKey key) where TObject : notnull where TKey : notnull @@ -3795,6 +3829,7 @@ public static void Remove(this ISourceCache source /// The source. /// The items. /// source. + /// public static void Remove(this ISourceCache source, IEnumerable items) where TObject : notnull where TKey : notnull @@ -3813,6 +3848,11 @@ public static void Remove(this ISourceCache source /// The source. /// The keys. /// source. + /// + /// + /// + /// + /// public static void Remove(this ISourceCache source, IEnumerable keys) where TObject : notnull where TKey : notnull @@ -3831,6 +3871,7 @@ public static void Remove(this ISourceCache source /// The source. /// The key. /// source. + /// public static void Remove(this IIntermediateCache source, TKey key) where TObject : notnull where TKey : notnull @@ -3849,6 +3890,7 @@ public static void Remove(this IIntermediateCache /// The source. /// The keys. /// source. + /// public static void Remove(this IIntermediateCache source, IEnumerable keys) where TObject : notnull where TKey : notnull @@ -3868,6 +3910,7 @@ public static void Remove(this IIntermediateCache /// The type of key. /// The source. /// An observable which emits change sets. + /// public static IObservable> RemoveKey(this IObservable> source) where TObject : notnull where TKey : notnull @@ -3891,6 +3934,7 @@ public static IObservable> RemoveKey(this IOb /// The source. /// The key. /// source. + /// public static void RemoveKey(this ISourceCache source, TKey key) where TObject : notnull where TKey : notnull @@ -4481,6 +4525,7 @@ public static IObservable> ToObservableChangeSetOptional instance used to determine if an object value has changed. /// An observable optional. /// source is null. + /// public static IObservable> ToObservableOptional(this IObservable> source, TKey key, IEqualityComparer? equalityComparer = null) where TObject : notnull where TKey : notnull @@ -4501,6 +4546,7 @@ public static IObservable> ToObservableOptional /// Optional instance used to determine if an object value has changed. /// An observable optional. /// source is null. + /// public static IObservable> ToObservableOptional(this IObservable> source, TKey key, bool initialOptionalWhenMissing, IEqualityComparer? equalityComparer = null) where TObject : notnull where TKey : notnull @@ -4529,6 +4575,7 @@ public static IObservable> ToObservableOptional /// The sort function. /// The sort order. Defaults to ascending. /// An observable which emits the read only collection. + /// public static IObservable> ToSortedCollection(this IObservable> source, Func sort, SortDirection sortOrder = SortDirection.Ascending) where TObject : notnull where TKey : notnull @@ -4542,6 +4589,7 @@ public static IObservable> ToSortedCollectionThe source. /// The sort comparer. /// An observable which emits the read only collection. + /// public static IObservable> ToSortedCollection(this IObservable> source, IComparer comparer) where TObject : notnull where TKey : notnull => source.QueryWhenChanged( @@ -5936,6 +5984,7 @@ static IEnumerable> ReplaceMoves(IChangeSet /// The equality condition. /// An observable which boolean values indicating if true. /// source. + /// public static IObservable TrueForAll(this IObservable> source, Func> observableSelector, Func equalityCondition) where TObject : notnull where TKey : notnull @@ -5959,6 +6008,7 @@ public static IObservable TrueForAll(this IObservab /// The equality condition. /// An observable which boolean values indicating if true. /// source. + /// public static IObservable TrueForAll(this IObservable> source, Func> observableSelector, Func equalityCondition) where TObject : notnull where TKey : notnull @@ -5984,6 +6034,7 @@ public static IObservable TrueForAll(this IObservab /// or /// equalityCondition. /// + /// public static IObservable TrueForAny(this IObservable> source, Func> observableSelector, Func equalityCondition) where TObject : notnull where TKey : notnull @@ -6009,6 +6060,7 @@ public static IObservable TrueForAny(this IObservab /// or /// equalityCondition. /// + /// public static IObservable TrueForAny(this IObservable> source, Func> observableSelector, Func equalityCondition) where TObject : notnull where TKey : notnull @@ -6058,6 +6110,7 @@ public static IObservable> Watch(this IObse /// The key. /// An observable which emits the object value. /// source. + /// public static IObservable WatchValue(this IObservableCache source, TKey key) where TObject : notnull where TKey : notnull @@ -6076,6 +6129,7 @@ public static IObservable WatchValue(this IObservableCac /// The key. /// An observable which emits the object value. /// source. + /// public static IObservable WatchValue(this IObservable> source, TKey key) where TObject : notnull where TKey : notnull diff --git a/src/DynamicData/List/ObservableListEx.cs b/src/DynamicData/List/ObservableListEx.cs index 2f871bf4..22d7f023 100644 --- a/src/DynamicData/List/ObservableListEx.cs +++ b/src/DynamicData/List/ObservableListEx.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2025 Roland Pheasant. All rights reserved. +// Copyright (c) 2011-2025 Roland Pheasant. All rights reserved. // Roland Pheasant licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -24,17 +24,29 @@ namespace DynamicData; public static class ObservableListEx { /// - /// Injects a side effect into a change set observable. + /// Injects a side effect into a changeset stream via an . + /// The adaptor's Adapt method is called for each changeset under a synchronized lock, then the changeset is forwarded downstream. /// - /// The type of the item. - /// The source. - /// The adaptor. - /// An observable which emits the change set. - /// - /// source - /// or - /// adaptor. - /// + /// The type of items in the list. + /// The source to observe and adapt. + /// The adaptor whose Adapt method is invoked for each changeset. + /// A list changeset stream identical to the source, with the adaptor side effect applied. + /// or is . + /// + /// + /// This operator synchronizes access with a lock, then calls adaptor.Adapt(changes) before forwarding the changeset. + /// It is the primary extension point for custom UI binding adaptors (e.g., + /// delegates to this operator). + /// + /// + /// EventBehavior + /// Add/AddRange/Replace/Remove/RemoveRange/Moved/Refresh/ClearThe adaptor's Adapt method is called with the full changeset, then it is forwarded downstream unchanged. + /// OnErrorForwarded to the downstream observer. If the adaptor throws, the exception propagates as OnError. + /// OnCompletedForwarded to the downstream observer. + /// + /// + /// + /// public static IObservable> Adapt(this IObservable> source, IChangeSetAdaptor adaptor) where T : notnull { @@ -55,16 +67,20 @@ public static IObservable> Adapt(this IObservable } /// - /// Adds a key to the change set result which enables all observable cache features of dynamic data. + /// Adds a key to each item in a list changeset, converting it to a cache changeset that supports all keyed DynamicData operators. /// + /// The type of items in the list. + /// The type of the key. + /// The source to add keys to, converting to a cache changeset. + /// A function to extract a unique key from each item. + /// A cache changeset stream with keyed items. /// - /// All indexed changes are dropped i.e. sorting is not supported by this function. + /// + /// All index information is dropped during conversion because cache changesets are unordered by default. + /// Use this when you need to transition from list-based pipelines to cache-based operators (Filter by key, Join, Group, etc.). + /// /// - /// The type of object. - /// The type of key. - /// The source. - /// The key selector. - /// An observable which emits the change set. + /// public static IObservable> AddKey(this IObservable> source, Func keySelector) where TObject : notnull where TKey : notnull @@ -76,13 +92,34 @@ public static IObservable> AddKey(this } /// - /// Apply a logical And operator between the collections. - /// Items which are in all of the sources are included in the result. + /// Applies a logical AND (intersection) between multiple list changeset streams. + /// Only items present in ALL sources appear in the result. /// - /// The type of the item. - /// The source. - /// The others. - /// An observable which emits the change set. + /// The type of items in the lists. + /// The primary source to intersect. + /// The additional changeset streams to intersect with. + /// A list changeset stream containing items that exist in every source. + /// + /// + /// Uses reference counting per item across all sources. An item appears downstream only when + /// its reference count is non-zero in ALL sources. Item identity is determined by the default equality comparer. + /// + /// + /// EventBehavior + /// Add/AddRangeThe item's reference count is incremented in its source tracker. If the item is now present in all sources, an Add is emitted. + /// ReplaceThe old item is removed from the tracker and the new item is added. Membership is recalculated for both. + /// Remove/RemoveRange/ClearThe item's reference count is decremented. If it was in the result and is no longer in all sources, a Remove is emitted. + /// RefreshForwarded as Refresh if the item is currently in the result. + /// MovedIgnored (set operations are position-independent). + /// OnErrorForwarded from any source. + /// OnCompletedForwarded when all sources complete. + /// + /// Worth noting: Item identity uses object equality, not position. Duplicate items in a single source are reference-counted independently. + /// + /// + /// + /// + /// public static IObservable> And(this IObservable> source, params IObservable>[] others) where T : notnull { @@ -91,53 +128,37 @@ public static IObservable> And(this IObservable> return source.Combine(CombineOperator.And, others); } - /// - /// Apply a logical And operator between the collections. - /// Items which are in all of the sources are included in the result. - /// - /// The type of the item. - /// The sources. - /// An observable which emits the change set. + /// + /// A of changeset streams to intersect. + /// This overload accepts a pre-built collection of sources instead of params array. public static IObservable> And(this ICollection>> sources) where T : notnull => sources.Combine(CombineOperator.And); - /// - /// Dynamically apply a logical And operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result. - /// - /// The type of the item. - /// The source. - /// An observable which emits the change set. + /// + /// An of changeset streams. Sources can be added or removed dynamically. + /// This overload supports dynamic source management: adding or removing changeset streams from the observable list triggers re-evaluation. public static IObservable> And(this IObservableList>> sources) where T : notnull => sources.Combine(CombineOperator.And); - /// - /// Dynamically apply a logical And operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result. - /// - /// The type of the item. - /// The source. - /// An observable which emits the change set. + /// + /// An of . Each inner list's changes are connected automatically. + /// This overload accepts instances directly, calling Connect() internally. public static IObservable> And(this IObservableList> sources) where T : notnull => sources.Combine(CombineOperator.And); - /// - /// Dynamically apply a logical And operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result. - /// - /// The type of the item. - /// The source. - /// An observable which emits the change set. + /// + /// An of . Each inner list's changes are connected automatically. + /// This overload accepts instances directly, calling Connect() internally. public static IObservable> And(this IObservableList> sources) where T : notnull => sources.Combine(CombineOperator.And); /// - /// Converts the source list to an read only observable list. + /// Wraps a as a read-only , hiding mutation methods. /// - /// The type of the item. - /// The source. - /// An observable list. - /// source. + /// The type of items in the list. + /// The mutable source list to wrap. + /// A read-only observable list that mirrors the source. + /// is . public static IObservableList AsObservableList(this ISourceList source) where T : notnull { @@ -147,12 +168,14 @@ public static IObservableList AsObservableList(this ISourceList source) } /// - /// Converts the source observable to an read only observable list. + /// Materializes a changeset stream into a read-only . + /// The list is kept in sync with the source stream for the lifetime of the subscription. /// - /// The type of the item. - /// The source. - /// An observable list. - /// source. + /// The type of items in the list. + /// The source to materialize into a read-only list. + /// A read-only observable list reflecting the current state of the stream. + /// is . + /// public static IObservableList AsObservableList(this IObservable> source) where T : notnull { @@ -162,14 +185,37 @@ public static IObservableList AsObservableList(this IObservable - /// Automatically refresh downstream operators when any property changes. + /// Monitors all properties on each item (via ) and emits Refresh + /// changes when any property changes, causing downstream operators to re-evaluate. /// - /// The type of object. - /// The source observable. - /// Batch up changes by specifying the buffer. This greatly increases performance when many elements have successive property changes. - /// When observing on multiple property changes, apply a throttle to prevent excessive refresh invocations. - /// The scheduler. - /// An observable change set with additional refresh changes. + /// The type of items, which must implement . + /// The source to monitor for property-driven refresh signals. + /// An optional buffer duration to batch multiple refresh signals into a single changeset. + /// An optional throttle applied to each item's property change notifications. + /// The scheduler for throttle and buffer timing. Defaults to . + /// A list changeset stream with additional Refresh changes injected when properties change. + /// + /// + /// Wraps using WhenAnyPropertyChanged() as the re-evaluator. + /// Pair with or + /// to get reactive re-evaluation on property changes. + /// + /// + /// EventBehavior + /// Add/AddRangeSubscribes to PropertyChanged on each new item. The original change is forwarded. + /// ReplaceUnsubscribes from the old item, subscribes to the new. The original change is forwarded. + /// Remove/RemoveRange/ClearUnsubscribes from removed items. The original change is forwarded. + /// Moved/RefreshForwarded unchanged. + /// Property changesA Refresh change is emitted for the item whose property changed. + /// OnErrorForwarded from the source or from the property change observable. + /// OnCompletedForwarded when the source completes. + /// + /// Worth noting: Each item generates a subscription. For large lists with frequent property changes, use and to reduce churn. + /// + /// + /// + /// + /// public static IObservable> AutoRefresh(this IObservable> source, TimeSpan? changeSetBuffer = null, TimeSpan? propertyChangeThrottle = null, IScheduler? scheduler = null) where TObject : INotifyPropertyChanged { @@ -189,17 +235,15 @@ public static IObservable> AutoRefresh(this IObserv scheduler); } - /// - /// Automatically refresh downstream operators when properties change. - /// - /// The type of object. - /// The type of property. - /// The source observable. - /// Specify a property to observe changes. When it changes a Refresh is invoked. - /// Batch up changes by specifying the buffer. This greatly increases performance when many elements have successive property changes. - /// When observing on multiple property changes, apply a throttle to prevent excessive refresh invocations. - /// The scheduler. - /// An observable change set with additional refresh changes. + /// + /// The type of items in the list. Must implement . + /// The type of the monitored property. + /// The source to monitor for property-driven refresh signals. + /// An expression selecting the specific property to monitor. + /// An optional buffer duration to batch refresh signals. + /// An optional throttle per item's property change notifications. + /// The for throttle and buffer timing. + /// This overload monitors a single property instead of all properties. More efficient when only one property affects downstream operators. public static IObservable> AutoRefresh(this IObservable> source, Expression> propertyAccessor, TimeSpan? changeSetBuffer = null, TimeSpan? propertyChangeThrottle = null, IScheduler? scheduler = null) where TObject : INotifyPropertyChanged { @@ -221,15 +265,36 @@ public static IObservable> AutoRefresh(t } /// - /// Automatically refresh downstream operator. The refresh is triggered when the observable receives a notification. + /// Monitors each item with a custom observable and emits Refresh changes whenever that observable fires, + /// causing downstream operators (Filter, Sort, Group) to re-evaluate. /// - /// The type of object. - /// A ignored type used for specifying what to auto refresh on. - /// The source observable change set. - /// An observable which acts on items within the collection and produces a value when the item should be refreshed. - /// Batch up changes by specifying the buffer. This greatly increases performance when many elements require a refresh. - /// The scheduler. - /// An observable change set with additional refresh changes. + /// The type of items in the list. + /// The type emitted by the re-evaluator observable (value is ignored). + /// The source to monitor for observable-driven refresh signals. + /// A factory that, given an item, returns an observable whose emissions trigger a Refresh for that item. + /// An optional buffer duration to batch refresh signals into a single changeset. + /// The for buffering. + /// A list changeset stream with additional Refresh changes injected when per-item observables fire. + /// + /// + /// This is the general-purpose refresh mechanism. + /// is a convenience wrapper that uses WhenAnyPropertyChanged() as the re-evaluator. + /// + /// + /// EventBehavior + /// Add/AddRangeSubscribes to the re-evaluator observable for each new item via MergeMany. The original change is forwarded. + /// ReplaceUnsubscribes from the old item's observable, subscribes to the new. The original change is forwarded. + /// Remove/RemoveRange/ClearUnsubscribes from removed items. The original change is forwarded. + /// Moved/RefreshForwarded unchanged. + /// Re-evaluator firesThe item's current index is looked up and a Refresh change is emitted. + /// OnErrorForwarded from source or from any re-evaluator observable. + /// OnCompletedForwarded when the source completes. + /// + /// Worth noting: The internal index lookup (to find where each item is for the Refresh change) requires maintaining a cloned list, adding memory overhead proportional to list size. + /// + /// + /// + /// public static IObservable> AutoRefreshOnObservable(this IObservable> source, Func> reevaluator, TimeSpan? changeSetBuffer = null, IScheduler? scheduler = null) where TObject : notnull { @@ -240,18 +305,38 @@ public static IObservable> AutoRefreshOnObservable - /// Binds a clone of the observable change set to the target observable collection. + /// Applies changeset mutations to a target for UI data binding. /// - /// The type of the item. - /// The source. - /// The target collection. - /// The reset threshold. - /// An observable which emits the change set. - /// - /// source - /// or - /// targetCollection. - /// + /// The type of items in the list. + /// The source to bind to a collection. + /// The target collection to keep in sync. + /// When a changeset exceeds this many changes, the collection is reset instead of applying individual changes. + /// A continuation of the source changeset stream (allows further chaining). + /// or is . + /// + /// + /// Delegates to with an an internal collection adaptor. + /// Each changeset is applied to the target collection on the calling thread. For UI binding, ensure the source is + /// observed on the UI thread (e.g., via ObserveOn). + /// + /// + /// EventBehavior + /// AddItem inserted at the specified index in the target collection. + /// AddRangeItems inserted as a range. If the count exceeds , the collection is cleared and repopulated. + /// ReplaceItem at the specified index is replaced. + /// RemoveItem at the specified index is removed. + /// RemoveRange/ClearItems removed from the collection. + /// MovedItem is moved between positions in the collection. + /// RefreshDepends on the adaptor implementation. + /// OnErrorForwarded to the downstream observer. + /// OnCompletedForwarded to the downstream observer. + /// + /// + /// + /// + /// + /// + /// public static IObservable> Bind(this IObservable> source, IObservableCollection targetCollection, int resetThreshold = BindingOptions.DefaultResetThreshold) where T : notnull { @@ -269,19 +354,11 @@ public static IObservable> Bind(this IObservable> return source.Bind(targetCollection, options); } - /// - /// Binds a clone of the observable change set to the target observable collection. - /// - /// The type of the item. - /// The source. - /// The target collection. - /// The binding options. - /// An observable which emits the change set. - /// - /// source - /// or - /// targetCollection. - /// + /// + /// The source to bind to a collection. + /// The target collection to keep in sync. + /// options controlling reset threshold and other behaviors. + /// This overload accepts a struct for fine-grained control over binding behavior. public static IObservable> Bind(this IObservable> source, IObservableCollection targetCollection, BindingOptions options) where T : notnull { @@ -292,14 +369,11 @@ public static IObservable> Bind(this IObservable> return source.Adapt(adaptor); } - /// - /// Creates a binding to a readonly observable collection which is specified as an 'out' parameter. - /// - /// The type of the item. - /// The source. - /// The resulting read only observable collection. - /// The reset threshold. - /// A continuation of the source stream. + /// + /// The source to bind to a collection. + /// An output parameter that receives the created , bound to the sorted results. + /// When a changeset exceeds this many changes, the collection is reset. + /// This overload creates a via an out parameter, backed by an internal ObservableCollectionExtended. public static IObservable> Bind(this IObservable> source, out ReadOnlyObservableCollection readOnlyObservableCollection, int resetThreshold = BindingOptions.DefaultResetThreshold) where T : notnull { @@ -315,14 +389,11 @@ public static IObservable> Bind(this IObservable> return source.Bind(out readOnlyObservableCollection, options); } - /// - /// Creates a binding to a readonly observable collection which is specified as an 'out' parameter. - /// - /// The type of the item. - /// The source. - /// The resulting read only observable collection. - /// The binding options. - /// A continuation of the source stream. + /// + /// The source to bind to a collection. + /// An output parameter that receives the created , bound to the sorted results. + /// options controlling reset threshold and other behaviors. + /// This overload creates a with for fine-grained control. public static IObservable> Bind(this IObservable> source, out ReadOnlyObservableCollection readOnlyObservableCollection, BindingOptions options) where T : notnull { @@ -337,19 +408,11 @@ public static IObservable> Bind(this IObservable> #if SUPPORTS_BINDINGLIST - /// - /// Binds a clone of the observable change set to the target observable collection. - /// - /// The type of the item. - /// The source. - /// The target binding list. - /// The reset threshold. - /// - /// source - /// or - /// targetCollection. - /// - /// An observable which emits the change set. + /// + /// The source to bind to a collection. + /// The target to keep in sync. + /// When a changeset exceeds this many changes, the list is reset. + /// This overload binds to a (WinForms binding). Uses a an internal binding list adaptor internally. public static IObservable> Bind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(this IObservable> source, BindingList bindingList, int resetThreshold = BindingOptions.DefaultResetThreshold) where T : notnull { @@ -361,30 +424,13 @@ public static IObservable> Bind(this IObservable> #endif - /// - /// Batches the underlying updates if a pause signal (i.e when the buffer selector return true) has been received. - /// When a resume signal has been received the batched updates will be fired. - /// - /// The type of the object. - /// The source. - /// When true, observable begins to buffer and when false, window closes and buffered result if notified. - /// The scheduler. - /// An observable which emits the change set. - /// source. + /// + /// This overload starts unpaused and has no timeout. public static IObservable> BufferIf(this IObservable> source, IObservable pauseIfTrueSelector, IScheduler? scheduler = null) where T : notnull => BufferIf(source, pauseIfTrueSelector, false, scheduler); - /// - /// Batches the underlying updates if a pause signal (i.e when the buffer selector return true) has been received. - /// When a resume signal has been received the batched updates will be fired. - /// - /// The type of the object. - /// The source. - /// When true, observable begins to buffer and when false, window closes and buffered result if notified. - /// if set to true [initial pause state]. - /// The scheduler. - /// An observable which emits the change set. - /// source. + /// + /// This overload allows setting the initial pause state but has no timeout. public static IObservable> BufferIf(this IObservable> source, IObservable pauseIfTrueSelector, bool initialPauseState, IScheduler? scheduler = null) where T : notnull { @@ -394,32 +440,39 @@ public static IObservable> BufferIf(this IObservable - /// Batches the underlying updates if a pause signal (i.e when the buffer selector return true) has been received. - /// When a resume signal has been received the batched updates will be fired. - /// - /// The type of the object. - /// The source. - /// When true, observable begins to buffer and when false, window closes and buffered result if notified. - /// Specify a time to ensure the buffer window does not stay open for too long. - /// The scheduler. - /// An observable which emits the change set. - /// source. + /// + /// This overload starts unpaused and accepts a timeout but not an explicit initial pause state. public static IObservable> BufferIf(this IObservable> source, IObservable pauseIfTrueSelector, TimeSpan? timeOut, IScheduler? scheduler = null) where T : notnull => BufferIf(source, pauseIfTrueSelector, false, timeOut, scheduler); /// - /// Batches the underlying updates if a pause signal (i.e when the buffer selector return true) has been received. - /// When a resume signal has been received the batched updates will be fired. + /// Buffers changeset notifications while a pause signal is active, then flushes all buffered changes when resumed. /// - /// The type of the object. - /// The source. - /// When true, observable begins to buffer and when false, window closes and buffered result if notified. - /// if set to true [initial pause state]. - /// Specify a time to ensure the buffer window does not stay open for too long. - /// The scheduler. - /// An observable which emits the change set. - /// source. + /// The type of items in the list. + /// The source to conditionally buffer. + /// An of that controls buffering: pauses (buffers), resumes (flushes). + /// The initial pause state. When , buffering starts immediately. + /// An optional maximum duration to keep the buffer open. After this time, the buffer is flushed regardless of pause state. + /// The for timeout scheduling. + /// A list changeset stream that buffers during pause and emits combined changesets on resume. + /// or is . + /// + /// + /// All changeset events are buffered at the changeset level (not individual changes) while paused. + /// On resume, all buffered changesets are emitted as a single combined changeset. If the buffer is empty on resume, + /// no emission occurs. + /// + /// + /// EventBehavior + /// Any (while paused)Accumulated in an internal buffer. Not emitted downstream. + /// Any (while active)Passed through immediately. + /// Pause selector emits falseAll buffered changesets are flushed downstream as one combined changeset. + /// Timeout firesAutomatically resumes and flushes the buffer. + /// OnErrorForwarded immediately (not buffered). + /// OnCompletedForwarded immediately. + /// + /// Worth noting: Each pause/resume cycle re-arms the timeout. Rapid toggling can create many small buffer windows. + /// public static IObservable> BufferIf(this IObservable> source, IObservable pauseIfTrueSelector, bool initialPauseState, TimeSpan? timeOut, IScheduler? scheduler = null) where T : notnull { @@ -430,13 +483,21 @@ public static IObservable> BufferIf(this IObservable - /// Buffers changes for an initial period only. After the period has elapsed, not further buffering occurs. + /// Buffers changesets during an initial time window, then emits a single combined changeset and passes through subsequent changes. /// - /// The type of object. - /// The source change set. - /// The period to buffer, measure from the time that the first item arrives. - /// The scheduler to buffer on. - /// An observable which emits the change set. + /// The type of items in the list. + /// The source to buffer during the initial loading period. + /// The time period (measured from first emission) during which changes are buffered. + /// The for timing the buffer window. + /// A list changeset stream where the initial burst is combined into one changeset. + /// + /// + /// Composed from , Buffer, and . + /// After the initial buffer period, all subsequent changesets pass through immediately. + /// + /// + /// + /// public static IObservable> BufferInitial(this IObservable> source, TimeSpan initialBuffer, IScheduler? scheduler = null) where TObject : notnull => source.DeferUntilLoaded().Publish( shared => @@ -447,11 +508,13 @@ public static IObservable> BufferInitial(this IObse }); /// - /// Cast the changes to another form. + /// Casts each item in the changeset from object to using a direct cast. /// - /// The type of the destination. - /// The source. - /// An observable which emits the change set. + /// The target type to cast to. + /// The source of object items. + /// A list changeset stream of cast items. + /// + /// public static IObservable> Cast(this IObservable> source) where TDestination : notnull { @@ -461,14 +524,16 @@ public static IObservable> Cast(this IObs } /// - /// Cast the changes to another form. - /// Alas, I had to add the converter due to type inference issues. The converter can be avoided by CastToObject() first. + /// Transforms each item in the changeset using a conversion function. /// - /// The type of the object. - /// The type of the destination. - /// The source. - /// The conversion factory. - /// An observable which emits the change set. + /// The source item type. + /// The destination item type. + /// The source to cast. + /// A function to convert each item from to . + /// A list changeset stream of converted items. + /// Use this overload when type inference requires explicit specification of both source and destination types. Alternatively, call first, then the single-type-parameter overload. + /// + /// public static IObservable> Cast(this IObservable> source, Func conversionFactory) where TSource : notnull where TDestination : notnull @@ -481,22 +546,28 @@ public static IObservable> Cast( } /// - /// Cast the underlying type of an object. Use before a Cast function. + /// Casts each item in the changeset to object. Typically used before to work around type inference limitations. /// - /// The type of the item. - /// The source. - /// An observable which emits the change set. + /// The source item type (must be a reference type). + /// The source to cast to object. + /// A list changeset stream of object items. + /// public static IObservable> CastToObject(this IObservable> source) where T : class => source.Select(changes => changes.Transform(t => (object)t)); /// - /// Clones the target list as a side effect of the stream. + /// Applies each changeset to the target list as a side effect, keeping it synchronized with the source. /// - /// The type of the item. - /// The source. - /// The target of the clone. - /// An observable which emits the change set. - /// source. + /// The type of items in the list. + /// The source to clone. + /// The target list to clone changes into. + /// A continuation of the source changeset stream. + /// is . + /// + /// Lower-level than . Uses .Clone() to apply all changeset operations directly. + /// + /// + /// public static IObservable> Clone(this IObservable> source, IList target) where T : notnull { @@ -511,8 +582,8 @@ public static IObservable> Clone(this IObservable /// /// The type of the object. /// The type of the destination. - /// The source. - /// The conversion factory. + /// The source to convert. + /// The conversion factory. /// An observable which emits the change set. [Obsolete("Prefer Cast as it is does the same thing but is semantically correct")] public static IObservable> Convert(this IObservable> source, Func conversionFactory) @@ -527,11 +598,27 @@ public static IObservable> Convert - /// Defer the subscription until the stream has been inflated with data. + /// Defers downstream delivery until the source emits its first changeset, then forwards all subsequent changesets. /// /// The type of the object. - /// The source. - /// An observable which emits the change set. + /// The source to defer until the first changeset arrives. + /// A list changeset stream that begins emitting only after the source has produced its first changeset. + /// is . + /// + /// + /// Subscribes to the source immediately but buffers internally until the first changeset arrives, at which point it emits + /// the initial data and all subsequent changesets. This is useful when downstream consumers should not receive an empty initial state. + /// + /// + /// EventBehavior + /// First changesetDelivered downstream, unlocking the stream for all future emissions. + /// Subsequent changesetsForwarded immediately. + /// OnErrorForwarded to the downstream observer. + /// OnCompletedForwarded to the downstream observer. + /// + /// + /// + /// public static IObservable> DeferUntilLoaded(this IObservable> source) where T : notnull { @@ -540,12 +627,10 @@ public static IObservable> DeferUntilLoaded(this IObservable(source).Run(); } - /// - /// Defer the subscription until the cache has been inflated with data. - /// - /// The type of the object. - /// The source. - /// An observable which emits the change set. + /// + /// + /// Convenience overload that calls source.Connect().DeferUntilLoaded(). + /// public static IObservable> DeferUntilLoaded(this IObservableList source) where T : notnull { @@ -555,16 +640,32 @@ public static IObservable> DeferUntilLoaded(this IObservableLis } /// - /// Disposes each item when no longer required. - /// - /// Individual items are disposed after removal or replacement changes have been sent downstream. - /// All items previously-published on the stream are disposed after the stream finalizes. - /// + /// Disposes items that implement when they are removed, replaced, or cleared from the stream. + /// All remaining tracked items are disposed when the stream finalizes (OnCompleted, OnError, or subscription disposal). /// /// The type of the object. - /// The source. - /// A continuation of the original stream. - /// source. + /// The source to track for disposal on removal. + /// A continuation of the source changeset stream with disposal side effects applied. + /// is . + /// + /// + /// Items are cast to and disposed after the changeset has been forwarded downstream. + /// Items that do not implement are silently ignored. + /// + /// + /// EventBehavior + /// Add/AddRangeItems are tracked for future disposal. Changeset forwarded. + /// ReplaceThe previous (replaced) item is disposed after the changeset is forwarded. The new item is tracked. + /// Remove/RemoveRangeRemoved items are disposed after the changeset is forwarded. + /// ClearAll tracked items are disposed after the changeset is forwarded. + /// Moved/RefreshForwarded. No disposal occurs. + /// OnError/OnCompleted/DisposalAll remaining tracked items are disposed during finalization. + /// + /// Worth noting: Disposal happens after the changeset is delivered downstream, so subscribers see the change before items are disposed. + /// + /// + /// + /// public static IObservable> DisposeMany(this IObservable> source) where T : notnull { @@ -574,18 +675,33 @@ public static IObservable> DisposeMany(this IObservable - /// Selects distinct values from the source, using the specified value selector. + /// Extracts distinct values from source items using , with reference counting to track when values enter and leave the result set. /// - /// The type of the source. - /// The type of the destination. - /// The source. - /// The transform factory. - /// An observable which emits the change set. - /// - /// source - /// or - /// valueSelector. - /// + /// The type of items in the source list. + /// The type of distinct values produced. + /// The source to extract distinct values. + /// A function that extracts the value to track from each source item. + /// A list changeset stream of distinct values. + /// or is . + /// + /// + /// Maintains an internal reference count per distinct value. A value is included when its count first exceeds zero + /// and removed when its count drops back to zero. + /// + /// + /// EventBehavior + /// Add/AddRangeValue extracted. If first occurrence, an Add is emitted. Otherwise the reference count is incremented silently. + /// ReplaceOld value's reference count decremented (removed if zero), new value's count incremented (added if first). If the value did not change, no emission. + /// Remove/RemoveRangeReference count decremented. If the count reaches zero, a Remove is emitted for that distinct value. + /// RefreshValue is re-extracted. If changed, old value decremented and new value incremented (same as Replace logic). + /// ClearAll reference counts cleared. Remove emitted for every tracked distinct value. + /// OnErrorForwarded to the downstream observer. + /// OnCompletedForwarded to the downstream observer. + /// + /// + /// + /// + /// public static IObservable> DistinctValues(this IObservable> source, Func valueSelector) where TObject : notnull where TValue : notnull @@ -598,13 +714,37 @@ public static IObservable> DistinctValues(th } /// - /// Apply a logical Except operator between the collections. - /// Items which are in the source and not in the others are included in the result. + /// Applies a logical set-difference (Except) between the source and other streams. + /// Items present in the first source but not in any of the are included in the result. /// /// The type of the item. - /// The source. - /// The others. - /// An observable which emits the change set. + /// The primary from which other streams are subtracted. + /// The other changeset streams to exclude from the result. + /// A list changeset stream containing items from that are not in any of . + /// is . + /// + /// + /// Uses reference-counted equality comparison across all sources. Items are compared by equality (not index position). + /// The first source has a special role: only items from it can appear in the result, and only if they do not exist in any other source. + /// + /// + /// EventBehavior + /// Add/AddRange (first source)If the item does not exist in any other source, an Add is emitted. + /// Add/AddRange (other source)If the item was in the result (from first source), a Remove is emitted. + /// Remove/RemoveRange/Clear (first source)If the item was in the result, a Remove is emitted. + /// Remove/RemoveRange/Clear (other source)If the item exists in the first source and no longer in any other, an Add is emitted. + /// ReplaceTreated as a Remove of the old item plus an Add of the new item, with set logic re-evaluated. + /// MovedIgnored by the set logic (no positional semantics). + /// RefreshForwarded if the item is currently in the result set. + /// OnErrorForwarded from any source. + /// OnCompletedForwarded when all sources have completed. + /// + /// Worth noting: Unlike , the first source is asymmetric: only its items can appear in the result. + /// + /// + /// + /// + /// public static IObservable> Except(this IObservable> source, params IObservable>[] others) where T : notnull { @@ -613,52 +753,57 @@ public static IObservable> Except(this IObservable - /// Apply a logical Except operator between the collections. - /// Items which are in the source and not in the others are included in the result. - /// - /// The type of the item. - /// The sources. - /// An observable which emits the change set. + /// + /// + /// Static overload accepting a pre-built collection of sources. The first item in the collection is the primary source. + /// public static IObservable> Except(this ICollection>> sources) where T : notnull => sources.Combine(CombineOperator.Except); - /// - /// Dynamically apply a logical Except operator. Items from the first observable list are included when an equivalent item does not exist in the other sources. - /// - /// The type of the item. - /// The source. - /// An observable which emits the change set. + /// + /// + /// Dynamic overload: sources can be added or removed from the at runtime. The first source in the list acts as the primary. + /// public static IObservable> Except(this IObservableList>> sources) where T : notnull => sources.Combine(CombineOperator.Except); - /// - /// Dynamically apply a logical Except operator. Items from the first observable list are included when an equivalent item does not exist in the other sources. - /// - /// The type of the item. - /// The source. - /// An observable which emits the change set. + /// + /// + /// Dynamic overload accepting of . Each inner list's Connect() is used as a source. + /// public static IObservable> Except(this IObservableList> sources) where T : notnull => sources.Combine(CombineOperator.Except); - /// - /// Dynamically apply a logical Except operator. Items from the first observable list are included when an equivalent item does not exist in the other sources. - /// - /// The type of the item. - /// The source. - /// An observable which emits the change set. + /// + /// + /// Dynamic overload accepting of . Each inner list's Connect() is used as a source. + /// public static IObservable> Except(this IObservableList> sources) where T : notnull => sources.Combine(CombineOperator.Except); /// - /// Removes items from the cache according to the value specified by the time selector function. + /// Automatically removes items from the list after the duration returned by . + /// Returns an observable of the items that were expired and removed. /// /// The type of the item. - /// The source. - /// Selector returning when to expire the item. Return null for non-expiring item. - /// Enter the polling interval to optimise expiry timers, if omitted 1 timer is created for each unique expiry time. - /// The scheduler. - /// An observable which emits the enumerable of items. + /// The source list to apply time-based expiration to. + /// A function returning the time-to-live for each item. Return for items that should never expire. + /// An optional polling interval to batch expiry checks. If omitted, a separate timer is created for each unique expiry time. + /// The scheduler for scheduling expiry timers. Defaults to . + /// An observable that emits collections of items each time expired items are removed from the source list. + /// + /// + /// This operator acts directly on an , not on a changeset stream. It monitors items as they are added, + /// schedules their removal, and physically removes them from the source list when their time expires. + /// + /// + /// When is specified, all items due for removal are batched into a single removal at each polling tick, + /// which can improve performance when many items expire around the same time. + /// + /// Worth noting: The returned observable emits the expired items (not changesets). Subscribe to this observable to trigger the expiry mechanism; if not subscribed, no items will be removed. + /// + /// + /// public static IObservable> ExpireAfter( this ISourceList source, Func timeSelector, @@ -672,14 +817,34 @@ public static IObservable> ExpireAfter( scheduler: scheduler); /// - /// Filters items, statically, in a list stream, based on a given predicate. + /// Filters items from the source list changeset stream using a static predicate. + /// Only items satisfying are included downstream. /// /// The type of items in the list. - /// The list stream whose items are to be filtered. - /// A static predicate to be used to determine which items should be included or excluded by the filter. - /// A list stream, containing only the items matched by . - /// Throws for and . - /// Note that, unlike some other overloads of this operator, ordering of items is preserved. + /// The source to filter. + /// A predicate that determines which items are included. Items returning appear downstream; items returning are excluded. + /// A list changeset stream containing only items that satisfy . + /// Thrown when or is . + /// + /// Use this overload when the predicate is fixed for the lifetime of the subscription. Item ordering is preserved. + /// + /// EventBehavior + /// AddThe predicate is evaluated. If the item passes, an Add is emitted at the calculated downstream index. Otherwise dropped. + /// AddRangeEach item in the range is evaluated. Matching items are emitted as an AddRange. + /// ReplaceThe predicate is re-evaluated. Four outcomes: both pass produces Replace; new passes but old didn't produces Add; old passed but new doesn't produces Remove; neither passes is dropped. + /// RemoveIf the item was included downstream, a Remove is emitted. Otherwise dropped. + /// RemoveRangeIncluded items in the range are emitted as individual Remove changes. + /// RefreshThe predicate is re-evaluated. If the item now passes but previously did not, an Add is emitted. If it previously passed but no longer does, a Remove is emitted. If still passes, the Refresh is forwarded. If still fails, dropped. + /// ClearAll downstream items are cleared. + /// OnErrorForwarded to the downstream observer. + /// OnCompletedForwarded to the downstream observer. + /// + /// Worth noting: Refresh events trigger re-evaluation, which can promote or demote items (turning a Refresh into an Add or Remove). Pair with for property-change-driven filtering. + /// + /// + /// + /// + /// public static IObservable> Filter( this IObservable> source, Func predicate) @@ -690,16 +855,36 @@ public static IObservable> Filter( suppressEmptyChangesets: true); /// - /// Filters source using the specified filter observable predicate. + /// Filters items using a dynamically changing predicate. + /// When emits a new function, all items are re-evaluated. /// /// The type of the item. - /// The source. - /// The predicate which indicates which items should be included. - /// Should the filter clear and replace, or calculate a diff-set. - /// An observable which emits the change set. - /// source - /// or - /// filterController. + /// The source to filter. + /// An that emits new predicate functions. Each emission triggers a full re-evaluation of all items. + /// The that controls re-filtering behavior: (default) computes the minimal diff between old and new results; clears and repopulates entirely. + /// A list changeset stream containing only items that satisfy the most recent predicate. + /// + /// + /// Each time emits, every item is re-evaluated. The controls + /// whether this produces a minimal diff (Add/Remove for items that changed status) or a full Clear+AddRange. + /// + /// + /// EventBehavior + /// AddThe current predicate is evaluated. If the item passes, an Add is emitted. Otherwise dropped. + /// AddRangeEach item is evaluated. Matching items are emitted as AddRange. + /// ReplaceRe-evaluated. Same four-outcome logic as the static overload (Replace, Add, Remove, or dropped). + /// RemoveIf the item was downstream, a Remove is emitted. Otherwise dropped. + /// RefreshRe-evaluated. If inclusion status changed, an Add or Remove is emitted. If unchanged, Refresh forwarded or dropped. + /// ClearAll downstream items are cleared. + /// Predicate changedAll items re-evaluated against the new predicate. With , only items that changed status emit Add/Remove. With , a Clear is emitted followed by AddRange of all matching items. + /// OnErrorForwarded from the source or from . + /// OnCompletedForwarded when the source completes. Independent completion of does not terminate the filter. + /// + /// Worth noting: No items are included until emits its first function. is generally preferred for performance; is useful when downstream consumers (like UI bindings) handle full resets more efficiently than individual changes. + /// + /// or is . + /// + /// public static IObservable> Filter(this IObservable> source, IObservable> predicate, ListFilterPolicy filterPolicy = ListFilterPolicy.CalculateDiff) where T : notnull { @@ -711,20 +896,38 @@ public static IObservable> Filter(this IObservable - /// Creates a filtered stream which can be dynamically filtered, based on state values passed through to a static filtering predicate. + /// Filters items using a predicate that receives external state. When emits a new state value, + /// all items are re-evaluated against using the updated state. /// /// The type of the item. /// The type of state value required by . - /// The source. - /// A stream of state values to be passed to . - /// A static predicate to be used to determine which items should be included or excluded by the filter. - /// The policy that the operator should use when performing re-filtering operations. - /// By default empty changeset notifications are suppressed for performance reasons. Set to false to publish empty changesets. Doing so can be useful for monitoring loading status. - /// An observable which emits change sets. - /// Throws for , , and . + /// The source to filter. + /// An stream of state values to be passed to . + /// A predicate receiving the current state and an item, returning to include or to exclude. + /// The that controls re-filtering behavior: (default) computes minimal diff; clears and repopulates. + /// When (default), empty changesets are suppressed. Set to to publish empty changesets (useful for monitoring loading status). + /// A list changeset stream containing only items satisfying with the current state. + /// , , or is . /// - /// Usually, should emit an initial value, immediately upon subscription. This is because cannot be invoked until the first state value is received, and accordingly, the operator will treat all items as excluded until then. Each value emitted by will trigger a full re-filtering of the entire collection, according to . + /// + /// The predicate cannot be invoked until the first state value is received. Until then, all items are treated as excluded. + /// Each subsequent state emission triggers a full re-evaluation of all items according to . + /// + /// + /// EventBehavior + /// Add/AddRangeEvaluated using current state. Matching items emitted as Add/AddRange. + /// ReplaceRe-evaluated. Same four-outcome logic as the static filter (Replace, Add, Remove, or dropped). + /// Remove/RemoveRangeIf the item was downstream, a Remove is emitted. + /// RefreshRe-evaluated against current state. Inclusion status may change. + /// ClearAll downstream items are cleared. + /// State changedAll items re-evaluated with new state value. emits minimal Add/Remove; emits Clear then AddRange. + /// OnErrorForwarded from the source or from . + /// OnCompletedForwarded when the source completes. + /// + /// Worth noting: should emit an initial value immediately upon subscription. No items are included until the first state value arrives. /// + /// + /// public static IObservable> Filter( this IObservable> source, IObservable predicateState, @@ -740,15 +943,40 @@ public static IObservable> Filter( suppressEmptyChangeSets: suppressEmptyChangeSets); /// - /// Filters source on the specified observable property using the specified predicate. - /// The filter will automatically reapply when a property changes. + /// Filters each item using a per-item of that dynamically controls inclusion. + /// When an item's observable emits the item enters the result; when it emits the item is removed. /// /// The type of the object. - /// The source. - /// The filter property selector. When the observable changes the filter will be re-evaluated. - /// The property changed throttle. - /// The scheduler used when throttling. - /// An observable which emits the change set. + /// The source to filter by property value. + /// A function that returns an observable of for each item, controlling its inclusion. + /// An optional throttle duration applied to each per-item observable to reduce re-evaluation frequency. + /// The used when throttling. Defaults to the system default scheduler. + /// A list changeset stream containing only items whose per-item observable most recently emitted . + /// or is . + /// + /// + /// Each item in the source gets its own subscription to the observable returned by . + /// The item's inclusion is determined by the most recent boolean value emitted by that observable. + /// + /// + /// Event (source)Behavior + /// Add/AddRangeSubscribes to the per-item observable. Item is included when it first emits . + /// ReplaceOld subscription disposed, new subscription created for the replacement item. + /// Remove/RemoveRange/ClearSubscription disposed. If the item was downstream, a Remove is emitted. + /// RefreshForwarded if the item is currently included. + /// OnErrorForwarded to the downstream observer. + /// OnCompletedForwarded to the downstream observer. + /// + /// + /// Event (per-item observable)Behavior + /// Emits If not already included, an Add is emitted downstream. + /// Emits If currently included, a Remove is emitted downstream. + /// + /// + /// + /// + /// + /// public static IObservable> FilterOnObservable(this IObservable> source, Func> objectFilterObservable, TimeSpan? propertyChangedThrottle = null, IScheduler? scheduler = null) where TObject : notnull { @@ -758,17 +986,21 @@ public static IObservable> FilterOnObservable(this } /// - /// Filters source on the specified property using the specified predicate. - /// The filter will automatically reapply when a property changes. + /// Filters items based on a property value, automatically re-evaluating when the specified property changes on any item. /// - /// The type of the object. + /// The type of the object. Must implement . /// The type of the property. - /// The source. - /// The property selector. When the property changes the filter specified will be re-evaluated. - /// A predicate based on the object which contains the changed property. - /// The property changed throttle. - /// The scheduler used when throttling. - /// An observable which emits the change set. + /// The source to filter by property value. + /// selecting the property to monitor for changes. + /// A predicate evaluated against the item to determine inclusion. + /// An optional throttle duration for property change notifications. + /// The used when throttling. + /// A list changeset stream of items satisfying the predicate, re-evaluated on property changes. + /// + /// Deprecated. Use followed by instead. + /// + /// + /// [Obsolete("Use AutoRefresh(), followed by Filter() instead")] public static IObservable> FilterOnProperty(this IObservable> source, Expression> propertySelector, Func predicate, TimeSpan? propertyChangedThrottle = null, IScheduler? scheduler = null) where TObject : INotifyPropertyChanged @@ -783,21 +1015,43 @@ public static IObservable> FilterOnProperty - /// Convert the result of a buffer operation to a change set. + /// Flattens a buffered list of changesets (from Rx's Buffer operator) back into a single changeset stream. + /// Empty buffers are dropped. /// /// The type of the item. - /// The source. - /// An observable which emits the change set. + /// The of buffered changeset lists. + /// A list changeset stream with all buffered changes concatenated into single changesets. + /// + /// Use this after applying Observable.Buffer() to a changeset stream to re-merge the batched changesets into a single stream. + /// + /// + /// public static IObservable> FlattenBufferResult(this IObservable>> source) where T : notnull => source.Where(x => x.Count != 0).Select(updates => new ChangeSet(updates.SelectMany(u => u))); /// - /// Provides a call back for each item change. + /// Invokes for every in each changeset, including range changes as-is. + /// The changeset is forwarded downstream unchanged. /// /// The type of the object. - /// The source. - /// The action. - /// An observable which emits the change set. + /// The source to observe each change in. + /// The action invoked for each . Range changes (AddRange, RemoveRange, Clear) are received as a single with a populated Range property. + /// A continuation of the source changeset stream. + /// or is . + /// + /// This is a side-effect operator. It does not modify the changeset. If you need each individual item from range operations flattened out, use instead. + /// + /// EventBehavior + /// Add/Replace/Remove/Moved/RefreshCallback invoked with the (single-item change). Changeset forwarded. + /// AddRange/RemoveRange/ClearCallback invoked once with the containing the range (accessible via Range property). Changeset forwarded. + /// OnErrorForwarded. If callback throws, propagates as OnError. + /// OnCompletedForwarded. + /// + /// + /// + /// + /// + /// public static IObservable> ForEachChange(this IObservable> source, Action> action) where TObject : notnull { @@ -809,13 +1063,21 @@ public static IObservable> ForEachChange(this IObse } /// - /// Provides a call back for each item change. - /// Range changes are flattened, so there is only need to check for Add, Replace, Remove and Clear. + /// Invokes for every individual in each changeset. + /// Range changes are flattened into individual item changes first, so the callback only receives Add, Replace, Remove, and Refresh. /// /// The type of the object. - /// The source. - /// The action. - /// An observable which emits the change set. + /// The source to observe each item-level change in. + /// The action invoked for each individual item change. + /// A continuation of the source changeset stream. + /// or is . + /// + /// + /// Unlike , this operator flattens + /// AddRange, RemoveRange, and Clear into individual entries before invoking the callback. + /// + /// + /// public static IObservable> ForEachItemChange(this IObservable> source, Action> action) where TObject : notnull { @@ -827,20 +1089,35 @@ public static IObservable> ForEachItemChange(this I } /// - /// Groups the source on the value returned by group selector factory. The groupings contains an inner observable list. + /// Groups source items by the value returned by . Each group is an + /// containing an inner observable list of its members. /// /// The type of the object. - /// The type of the group. - /// The source. - /// The group selector. - /// Force the grouping function to recalculate the group value. - /// For example if you have a time based grouping with values like `Last Minute', 'Last Hour', 'Today' etc regrouper is used to refresh these groupings. - /// An observable which emits the change set. - /// - /// source - /// or - /// groupSelector. - /// + /// The type of the group key. + /// The source to group. + /// A function that returns the group key for each item. + /// An optional of that forces all items to be re-evaluated against when it fires. Useful for time-based groupings (e.g., "Last Hour", "Today"). + /// A list changeset stream of objects, each containing the items belonging to that group. + /// or is . + /// + /// + /// Groups are created lazily and removed when empty. Each group exposes an inner observable list that receives incremental updates. + /// + /// + /// EventBehavior + /// Add/AddRangeGroup key evaluated. Item added to its group. If the group is new, an Add of the group is emitted. + /// ReplaceGroup key re-evaluated. If the group changed, the item is removed from the old group and added to the new one. Empty old groups are removed. + /// Remove/RemoveRange/ClearItem removed from its group. Empty groups are removed from the result. + /// RefreshGroup key re-evaluated. If changed, the item moves between groups. + /// MovedNot handled by group logic. + /// Regrouper firesAll items re-evaluated. Items that changed group key are moved between groups. Empty groups removed, new groups added. + /// OnErrorForwarded to the downstream observer. + /// OnCompletedForwarded to the downstream observer. + /// + /// + /// + /// + /// public static IObservable>> GroupOn(this IObservable> source, Func groupSelector, IObservable? regrouper = null) where TObject : notnull where TGroup : notnull @@ -853,17 +1130,26 @@ public static IObservable>> GroupOn - /// Groups the source using the property specified by the property selector. The resulting groupings contains an inner observable list. - /// Groups are re-applied when the property value changed. - /// When there are likely to be a large number of group property changes specify a throttle to improve performance. + /// Groups items by a property value, automatically re-grouping when the specified property changes on any item. + /// Each group contains an inner observable list. /// - /// The type of the object. - /// The type of the group. - /// The source. - /// The property selector used to group the items. - /// The property changed throttle. - /// The scheduler. - /// An observable which emits the change set. + /// The type of the object. Must implement . + /// The type of the group key. + /// The source to group by property value. + /// selecting the property whose value determines the group key. + /// An optional throttle duration for property change notifications. + /// The used when throttling. + /// A list changeset stream of objects. + /// or is . + /// + /// + /// Convenience operator equivalent to .AutoRefresh(propertySelector).GroupOn(item => property). + /// Property changes trigger re-evaluation of the group key, potentially moving items between groups. + /// + /// + /// + /// + /// public static IObservable>> GroupOnProperty(this IObservable> source, Expression> propertySelector, TimeSpan? propertyChangedThrottle = null, IScheduler? scheduler = null) where TObject : INotifyPropertyChanged where TGroup : notnull @@ -876,17 +1162,27 @@ public static IObservable>> GroupOnProperty - /// Groups the source using the property specified by the property selector. The resulting groupings are immutable. - /// Groups are re-applied when the property value changed. - /// When there are likely to be a large number of group property changes specify a throttle to improve performance. + /// Groups items by a property value, automatically re-grouping when the specified property changes. + /// Each group emits immutable snapshots (not live observable lists). /// - /// The type of the object. - /// The type of the group. - /// The source. - /// The property selector used to group the items. - /// The property changed throttle. - /// The scheduler. - /// An observable which emits the change set. + /// The type of the object. Must implement . + /// The type of the group key. + /// The source to group by property value with immutable snapshots. + /// selecting the property whose value determines the group key. + /// An optional throttle duration for property change notifications. + /// The used when throttling. + /// A list changeset stream of immutable group snapshots. + /// or is . + /// + /// + /// Combines + /// with . + /// Unlike , + /// this produces immutable snapshots per group rather than live inner observable lists. + /// + /// + /// + /// public static IObservable>> GroupOnPropertyWithImmutableState(this IObservable> source, Expression> propertySelector, TimeSpan? propertyChangedThrottle = null, IScheduler? scheduler = null) where TObject : INotifyPropertyChanged where TGroup : notnull @@ -899,20 +1195,25 @@ public static IObservable>> GroupOnProperty - /// Groups the source on the value returned by group selector factory. Each update produces immutable grouping. + /// Groups source items by the value returned by . Each update produces immutable grouping snapshots + /// rather than live inner observable lists. /// /// The type of the object. /// The type of the group key. - /// The source. - /// The group selector key. - /// Force the grouping function to recalculate the group value. - /// For example if you have a time based grouping with values like `Last Minute', 'Last Hour', 'Today' etc regrouper is used to refresh these groupings. - /// An observable which emits the change set. - /// - /// source - /// or - /// groupSelectorKey. - /// + /// The source to group with immutable snapshots. + /// A function that returns the group key for each item. + /// An optional of that forces all items to be re-evaluated when it fires. + /// A list changeset stream of immutable snapshots. + /// or is . + /// + /// + /// Works like + /// but each affected group emits a new immutable snapshot on every change rather than updating a live inner list. + /// This is useful when consumers need thread-safe, point-in-time snapshots of each group. + /// + /// + /// + /// public static IObservable>> GroupWithImmutableState(this IObservable> source, Func groupSelectorKey, IObservable? regrouper = null) where TObject : notnull where TGroupKey : notnull @@ -925,16 +1226,26 @@ public static IObservable>> GroupOnProperty - /// Limits the size of the source cache to the specified limit. - /// Notifies which items have been removed from the source list. + /// Limits the source list to a maximum number of items using FIFO eviction. + /// When the list exceeds , the oldest items are removed. + /// Returns an observable of the items that were removed. /// /// The type of the item. - /// The source. - /// The size limit. - /// The scheduler. - /// An observable which emits a enumerable of items. - /// source. - /// sizeLimit cannot be zero. + /// The source list to apply size limits to. + /// The maximum number of items allowed. Must be greater than zero. + /// The scheduler for scheduling size checks. Defaults to . + /// An observable that emits collections of items each time excess items are removed from the source list. + /// is . + /// is zero or negative. + /// + /// + /// This operator acts directly on an . It subscribes to the source's changes, + /// tracks insertion order using an internal Transform, and removes the oldest items when the size limit is exceeded. + /// + /// Worth noting: The returned observable emits the removed items (not changesets). Subscribe to this observable to activate the size-limiting mechanism. Removal is performed synchronously under a lock shared with the change tracking. + /// + /// + /// public static IObservable> LimitSizeTo(this ISourceList source, int sizeLimit, IScheduler? scheduler = null) where T : notnull { @@ -952,17 +1263,34 @@ public static IObservable> LimitSizeTo(this ISourceList sou } /// - /// Dynamically merges the observable which is selected from each item in the stream, and un-merges the item - /// when it is no longer part of the stream. + /// Subscribes to a per-item observable for each item in the source and merges all emissions into a single stream. + /// This is NOT a changeset operator: it returns a flat observable of values. /// - /// The type of the object. - /// The type of the destination. - /// The source. - /// The observable selector. - /// An observable which emits the destination value. - /// source - /// or - /// observableSelector. + /// The type of items in the source list. + /// The type of values emitted by per-item observables. + /// The source whose items each produce an observable. + /// A function that returns an observable for each source item. + /// An observable that emits values from all per-item observables, merged together. + /// or is . + /// + /// + /// Internally uses to manage per-item subscriptions. + /// When an item is added, a new subscription is created via . When removed or replaced, the old subscription is disposed. + /// + /// + /// Event (source)Subscription behavior + /// Add/AddRangeSubscribes to the per-item observable. Emissions are merged into the output. + /// ReplaceOld subscription disposed, new subscription created for the replacement item. + /// Remove/RemoveRange/ClearSubscription disposed. + /// Refresh/MovedNo effect on subscriptions. + /// OnCompleted (source)Completes only after the source and all active inner observables have completed. + /// OnErrorForwarded from the source or from any per-item observable. + /// + /// + /// + /// + /// + /// public static IObservable MergeMany(this IObservable> source, Func> observableSelector) where T : notnull { @@ -974,13 +1302,36 @@ public static IObservable MergeMany(this IObserva } /// - /// Operator similiar to Merge except it is ChangeSet aware. All of the observable changesets are merged together into a single stream of ChangeSet events. + /// Merges multiple list changeset streams from an observable-of-observables into a single unified changeset stream. + /// Unlike cache MergeChangeSets, list merging performs no key-based deduplication. /// /// The type of the object. - /// The Source Observable ChangeSet. - /// instance to determine if two elements are the same. - /// The result from merging the changesets together. - /// Parameter was null. + /// The source of nested changeset observables. + /// An optional used by the merge tracker to compare items. + /// A single list changeset stream containing all changes from all inner streams. + /// is . + /// + /// + /// All changes from inner streams are forwarded to the output. + /// Replace changes are decomposed into a Remove of the old item followed by an Add of the new item. + /// Moved changes from inner streams are ignored. + /// + /// + /// EventBehavior + /// Add/AddRangeForwarded to the merged output. + /// ReplaceThe old value is replaced by the new value in the merged output. If the old value is not found (by reference), the new value is added instead. + /// Remove/RemoveRange/ClearForwarded to the merged output. + /// RefreshForwarded to the merged output. + /// MovedIgnored. + /// OnErrorForwarded from any inner source. + /// OnCompletedForwarded when all inner sources have completed. + /// + /// Worth noting: There is no key-based deduplication. If the same item appears in multiple inner streams, it will appear multiple times in the merged output. + /// + /// + /// + /// + /// public static IObservable> MergeChangeSets(this IObservable>> source, IEqualityComparer? equalityComparer = null) where TObject : notnull { @@ -989,17 +1340,15 @@ public static IObservable> MergeChangeSets(this IOb return new MergeChangeSets(source, equalityComparer).Run(); } + /// /// - /// Operator similiar to Merge except it is ChangeSet aware. Merges both observable changesets into a single stream of ChangeSet events. + /// Merges two list changeset streams into a single unified stream. /// - /// The type of the object. - /// The Source Observable ChangeSet. - /// The Other Observable ChangeSet. - /// instance to determine if two elements are the same. - /// (Optional) instance to use when enumerating the collection. - /// Whether or not the result Observable should complete if all the changesets complete. - /// The result from merging the changesets together. - /// Parameter was null. + /// The first to merge. + /// The second to merge with. + /// An optional used to compare items. + /// An optional for scheduling enumeration. + /// When (default), the result completes when all sources complete. public static IObservable> MergeChangeSets(this IObservable> source, IObservable> other, IEqualityComparer? equalityComparer = null, IScheduler? scheduler = null, bool completable = true) where TObject : notnull { @@ -1009,17 +1358,15 @@ public static IObservable> MergeChangeSets(this IOb return new[] { source, other }.MergeChangeSets(equalityComparer, scheduler, completable); } + /// /// - /// Operator similiar to Merge except it is ChangeSet aware. Merges the source changeset and the collection of other changesets together into a single stream of ChangeSet events. + /// Merges the source list changeset stream with additional changeset streams into a single unified stream. /// - /// The type of the object. - /// The Source Observable ChangeSet. - /// The Other Observable ChangeSets. - /// instance to determine if two elements are the same. - /// (Optional) instance to use when enumerating the collection. - /// Whether or not the result Observable should complete if all the changesets complete. - /// The result from merging the changesets together. - /// Parameter was null. + /// The primary source to merge. + /// The additional of list changeset streams to merge with. + /// An optional used to compare items. + /// An optional for scheduling enumeration. + /// When (default), the result completes when all sources complete. public static IObservable> MergeChangeSets(this IObservable> source, IEnumerable>> others, IEqualityComparer? equalityComparer = null, IScheduler? scheduler = null, bool completable = true) where TObject : notnull { @@ -1030,15 +1377,24 @@ public static IObservable> MergeChangeSets(this IOb } /// - /// Operator similiar to Merge except it is ChangeSet aware. All of the observable changesets are merged together into a single stream of ChangeSet events. + /// Merges a collection of list changeset streams into a single unified changeset stream. + /// This is the primary overload that all other list MergeChangeSets overloads delegate to. /// /// The type of the object. - /// The Source Observable ChangeSet. - /// instance to determine if two elements are the same. - /// (Optional) instance to use when enumerating the collection. - /// Whether or not the result Observable should complete if all the changesets complete. - /// The result from merging the changesets together. - /// Parameter was null. + /// The collection of list changeset streams to merge. + /// An optional used by the merge tracker to compare items. + /// An optional for scheduling enumeration. + /// When (default), the result completes when all sources complete. + /// A single list changeset stream containing all changes from all sources. + /// is . + /// + /// + /// Replace changes from inner streams are handled as a replace-or-add: if the old item is found in the merged output, it is replaced; otherwise the new item is added. Moved changes from inner streams are ignored. + /// There is no key-based deduplication (unlike cache MergeChangeSets). + /// + /// + /// + /// public static IObservable> MergeChangeSets(this IEnumerable>> source, IEqualityComparer? equalityComparer = null, IScheduler? scheduler = null, bool completable = true) where TObject : notnull { @@ -1047,14 +1403,10 @@ public static IObservable> MergeChangeSets(this IEn return new MergeChangeSets(source, equalityComparer, completable, scheduler).Run(); } + /// /// - /// Merges all of the Cache Observable ChangeSets into a single ChangeSets that correctly handles removal of the parent items. + /// Merges list changeset streams from an into a single stream. Sources can be added or removed dynamically. /// - /// The type of the object. - /// The SourceList of Observable Cache ChangeSets. - /// Optional instance to determine if two elements are the same. - /// The result from merging the child changesets together. - /// Parameter was null. public static IObservable> MergeChangeSets(this IObservableList>> source, IEqualityComparer? equalityComparer = null) where TObject : notnull { @@ -1063,14 +1415,11 @@ public static IObservable> MergeChangeSets(this IOb return source.Connect().MergeChangeSets(equalityComparer); } + /// /// - /// Merges each Observable ChangeSet in the ObservableList into a single stream of ChangeSets that correctly handles removal of the parent items. + /// Merges list changeset streams from a list-of-list-changeset-observables into a single stream. + /// Each inner list changeset observable in the source list is merged, and parent item removal triggers child cleanup. /// - /// The type of the object. - /// The List Observable ChangeSet of Cache Observable ChangeSets. - /// Optional instance to determine if two elements are the same. - /// The result from merging the child changesets together. - /// Parameter was null. public static IObservable> MergeChangeSets(this IObservable>>> source, IEqualityComparer? equalityComparer = null) where TObject : notnull { @@ -1080,14 +1429,20 @@ public static IObservable> MergeChangeSets(this IOb } /// - /// Merges each Observable ChangeSet in the ObservableList into a single stream of ChangeSets that correctly handles multiple Keys and removal of the parent items. + /// Merges cache changeset streams from an into a single cache changeset stream. + /// Uses to resolve conflicts when the same key appears in multiple child streams. /// /// The type of the object. /// The type of the object key. - /// The SourceList of Observable Cache ChangeSets. - /// instance to determine which element to emit if the same key is emitted from multiple child changesets. - /// The result from merging the child changesets together. - /// Parameter was null. + /// The of cache changeset observables. + /// to resolve which value wins when the same key appears in multiple sources. + /// A single cache changeset stream with key-based deduplication. + /// is . + /// + /// Sources can be added or removed dynamically from the observable list. Parent item removal triggers cleanup of all child items from that source. + /// + /// + /// public static IObservable> MergeChangeSets(this IObservableList>> source, IComparer comparer) where TObject : notnull where TKey : notnull @@ -1097,16 +1452,13 @@ public static IObservable> MergeChangeSets /// - /// Merges all of the Cache Observable ChangeSets into a single ChangeSets while correctly handling multiple Keys and removal of the parent items. + /// Merges cache changeset streams from an into a single cache changeset stream, with optional equality and ordering comparers. /// - /// The type of the object. - /// The type of the object key. - /// The SourceList of Observable Cache ChangeSets. - /// Optional instance to determine if two elements are the same. - /// Optional instance to determine which element to emit if the same key is emitted from multiple child changesets. - /// The result from merging the child changesets together. - /// Parameter was null. + /// The of cache changeset observables. + /// An optional to determine if two elements are the same. + /// An optional to resolve conflicts when the same key appears in multiple sources. public static IObservable> MergeChangeSets(this IObservableList>> source, IEqualityComparer? equalityComparer = null, IComparer? comparer = null) where TObject : notnull where TKey : notnull @@ -1116,15 +1468,12 @@ public static IObservable> MergeChangeSets /// - /// Merges all of the Cache Observable ChangeSets into a single ChangeSets while correctly handling multiple Keys and removal of the parent items. + /// Merges cache changeset streams from a list changeset of cache changeset observables, using a comparer for conflict resolution. /// - /// The type of the object. - /// The type of the object key. - /// The List Observable ChangeSet of Cache Observable ChangeSets. - /// instance to determine which element to emit if the same key is emitted from multiple child changesets. - /// The result from merging the child changesets together. - /// Parameter was null. + /// The source whose items are cache changeset observables. + /// to resolve which value wins when the same key appears in multiple sources. public static IObservable> MergeChangeSets(this IObservable>>> source, IComparer comparer) where TObject : notnull where TKey : notnull @@ -1134,16 +1483,13 @@ public static IObservable> MergeChangeSets /// - /// Merges each Observable ChangeSet in the ObservableList into a single stream of ChangeSets that correctly handles multiple Keys and removal of the parent items. + /// Merges cache changeset streams from a list changeset of cache changeset observables, with optional equality and ordering comparers. /// - /// The type of the object. - /// The type of the object key. - /// The List Observable ChangeSet of Cache Observable ChangeSets. - /// Optional instance to determine if two elements are the same. - /// Optional instance to determine which element to emit if the same key is emitted from multiple child changesets. - /// The result from merging the child changesets together. - /// Parameter was null. + /// The source whose items are cache changeset observables. + /// An optional to determine if two elements are the same. + /// An optional to resolve conflicts when the same key appears in multiple sources. public static IObservable> MergeChangeSets(this IObservable>>> source, IEqualityComparer? equalityComparer = null, IComparer? comparer = null) where TObject : notnull where TKey : notnull @@ -1154,15 +1500,34 @@ public static IObservable> MergeChangeSets - /// Operator similiar to MergeMany except it is List ChangeSet aware. It uses to transform each item in the source into a child and merges the result children together into a single stream of ChangeSets that correctly handles removal of the parent items and other changes to the source list. + /// Transforms each source item into a child list changeset stream using , + /// then merges all child streams into a single flat list changeset stream. Parent item removal cleans up all associated children. /// - /// The type of the object. - /// The type of the destination. - /// The Source Observable ChangeSet. - /// Factory Function used to create child changesets. - /// Optional instance to determine if two elements are the same. - /// The result from merging the children list changesets together. - /// Parameter was null. + /// The type of items in the source list. + /// The type of items in the child changeset streams. + /// The source whose items each produce a child changeset stream. + /// A function that returns a child list changeset stream for each source item. + /// An optional used to compare child items. + /// A single list changeset stream containing all items from all child streams. + /// or is . + /// + /// + /// Internally subscribes to each child stream when a source item is added and disposes the subscription when it is removed. + /// All child items from a removed parent are removed from the merged output. + /// + /// + /// Event (source)Behavior + /// Add/AddRangeSubscribes to the child stream. Child emissions are merged into the output. + /// ReplaceOld child subscription disposed (and its items removed from output). New child subscription created. + /// Remove/RemoveRange/ClearChild subscription disposed. All child items from that parent are removed. + /// OnErrorForwarded from the source or any child stream. + /// OnCompletedForwarded when the source completes. + /// + /// + /// + /// + /// + /// public static IObservable> MergeManyChangeSets(this IObservable> source, Func>> observableSelector, IEqualityComparer? equalityComparer = null) where TObject : notnull where TDestination : notnull @@ -1181,16 +1546,21 @@ public static IObservable> MergeManyChangeSets - /// Operator similiar to MergeMany except it is Cache ChangeSet aware. It uses to transform each item in the source into a child and merges the result children together into a single stream of ChangeSets that correctly handles multiple Keys and removal of the parent items. + /// Transforms each source item into a child cache changeset stream and merges all children into a single cache changeset stream. + /// Uses to resolve key conflicts when the same key appears in multiple child streams. /// - /// The type of the object. - /// The type of the destination. - /// The type of the destination key. - /// The Source Observable ChangeSet. - /// Factory Function used to create child changesets. - /// instance to determine which element to emit if the same key is emitted from multiple child changesets. - /// The result from merging the child changesets together. - /// Parameter was null. + /// The type of items in the source list. + /// The type of items in the child cache changeset streams. + /// The type of the key in the child cache changesets. + /// The source whose items each produce a child changeset stream. + /// A function that returns a child cache changeset stream for each source item. + /// to resolve which value wins when the same key appears from multiple children. + /// A single cache changeset stream with key-based deduplication. + /// , , or is . + /// + /// Delegates to with a equality comparer. + /// + /// public static IObservable> MergeManyChangeSets(this IObservable> source, Func>> observableSelector, IComparer comparer) where TObject : notnull where TDestination : notnull @@ -1204,17 +1574,27 @@ public static IObservable> MergeManyCh } /// - /// Operator similiar to MergeMany except it is Cache ChangeSet aware. It uses to transform each item in the source into a child and merges the result children together into a single stream of ChangeSets that correctly handles multiple Keys and removal of the parent items. + /// Transforms each source item into a child cache changeset stream and merges all children into a single cache changeset stream. + /// This is the primary list-to-cache MergeManyChangeSets overload. /// - /// The type of the object. - /// The type of the destination. - /// The type of the destination key. - /// The Source Observable ChangeSet. - /// Factory Function used to create child changesets. - /// Optional instance to determine if two elements are the same. - /// Optional instance to determine which element to emit if the same key is emitted from multiple child changesets. - /// The result from merging the child changesets together. - /// Parameter was null. + /// The type of items in the source list. + /// The type of items in the child cache changeset streams. + /// The type of the key in the child cache changesets. + /// The source whose items each produce a child changeset stream. + /// A function that returns a child cache changeset stream for each source item. + /// An optional to determine if two elements are the same. + /// An optional to resolve conflicts when the same key appears from multiple children. + /// A single cache changeset stream with key-based deduplication. + /// or is . + /// + /// + /// Each source item produces a keyed child stream via . All child items are tracked by key. + /// When a parent item is removed, all its child items are removed from the merged output. + /// When the same key appears from multiple children, determines which value wins. + /// + /// + /// + /// public static IObservable> MergeManyChangeSets(this IObservable> source, Func>> observableSelector, IEqualityComparer? equalityComparer = null, IComparer? comparer = null) where TObject : notnull where TDestination : notnull @@ -1227,12 +1607,14 @@ public static IObservable> MergeManyCh } /// - /// Prevents an empty notification. + /// Suppresses empty changesets from the stream. Only changesets with at least one change are forwarded. /// /// The type of the item. - /// The source. - /// An observable which emits the change set. - /// source. + /// The source to suppress empty changesets. + /// A list changeset stream with empty changesets filtered out. + /// is . + /// + /// public static IObservable> NotEmpty(this IObservable> source) where T : notnull { @@ -1242,14 +1624,31 @@ public static IObservable> NotEmpty(this IObservable - /// Invokes a given action for every item added to the source list stream. + /// Invokes for every item added to the source list stream. + /// Triggers on , , and the new item of . /// /// The type of items in the list. - /// The list stream whose items are to be passed to . - /// The action to invoke upon each added item. - /// A list stream, containing all items in , with changes published after has been invoked. - /// Throws for and . - /// Note that "added" items includes items from , , and changes. + /// The source to observe item additions in. + /// The action to invoke for each added item. + /// A continuation of the source changeset stream, with the side effect applied before forwarding. + /// or is . + /// + /// The action fires before the changeset is forwarded downstream. + /// + /// EventBehavior + /// AddCallback invoked with the added item. Changeset forwarded. + /// AddRangeCallback invoked for each item in the range. Changeset forwarded. + /// ReplaceCallback invoked for the new (replacement) item. Changeset forwarded. + /// Remove/RemoveRange/ClearNo callback. Changeset forwarded. + /// Moved/RefreshNo callback. Changeset forwarded. + /// OnErrorForwarded. If callback throws, propagates as OnError. + /// OnCompletedForwarded. + /// + /// + /// + /// + /// + /// public static IObservable> OnItemAdded( this IObservable> source, Action addAction) @@ -1259,14 +1658,17 @@ public static IObservable> OnItemAdded( addAction: addAction); /// - /// Invokes a given action for every item refreshed within the source list stream. + /// Invokes for every item with a change in the source stream. /// /// The type of items in the list. - /// The list stream whose items are to be passed to . - /// The action to invoke upon each refreshed item. - /// A list stream, containing all items in , with changes published after has been invoked. - /// Throws for and . - /// Note that "refreshed" items refers to items from changes. + /// The source to observe item refresh events in. + /// The action to invoke for each refreshed item. + /// A continuation of the source changeset stream, with the side effect applied before forwarding. + /// or is . + /// + /// + /// + /// public static IObservable> OnItemRefreshed( this IObservable> source, Action refreshAction) @@ -1276,15 +1678,36 @@ public static IObservable> OnItemRefreshed( refreshAction: refreshAction); /// - /// Invokes a given action for every item removed from the source list stream. + /// Invokes for every item removed from the source list stream. + /// Triggers on , , , and the old item of . /// /// The type of items in the list. - /// The list stream whose items are to be passed to . - /// The action to invoke upon each removed item. - /// A flag indicating whether should be invoked upon teardown of the stream. This includes disposal of subscriptions, completion notifications, and error notifications. - /// A list stream, containing all items in , with changes published after has been invoked. - /// Throws for and . - /// Note that "removed" items includes items from , , , and changes. + /// The source to observe item removals in. + /// The action to invoke for each removed item. + /// When (default), is also invoked for all remaining tracked items upon stream disposal, completion, or error. + /// A continuation of the source changeset stream, with the side effect applied before forwarding. + /// or is . + /// + /// + /// When is , the operator tracks all items that have been added but not yet removed, + /// and fires for each of them during finalization. This is useful for resource cleanup patterns. + /// + /// + /// EventBehavior + /// Add/AddRangeTracked internally (when is ). No callback invoked. Changeset forwarded. + /// ReplaceCallback invoked for the previous (replaced) item. New item tracked. Changeset forwarded. + /// RemoveCallback invoked for the removed item. Changeset forwarded. + /// RemoveRange/ClearCallback invoked for each removed item. Changeset forwarded. + /// Moved/RefreshNo callback. Changeset forwarded. + /// OnErrorIf is , callback is invoked for all tracked items before the error propagates. + /// OnCompletedIf is , callback is invoked for all tracked items before completion propagates. + /// + /// Worth noting: When is (the default), disposing the subscription also invokes the callback for every item still in the list, not just items that were explicitly removed during the subscription. Exceptions in are not caught. + /// + /// + /// + /// + /// public static IObservable> OnItemRemoved( this IObservable> source, Action removeAction, @@ -1295,24 +1718,43 @@ public static IObservable> OnItemRemoved( removeAction: removeAction, invokeOnUnsubscribe: invokeOnUnsubscribe); + /// /// - /// Apply a logical Or operator between the collections. - /// Items which are in any of the sources are included in the result. + /// Applies a logical OR (union) between a pre-built collection of list changeset sources. Items present in any source are included. /// - /// The type of the item. - /// The source. - /// An observable which emits the change set. + /// public static IObservable> Or(this ICollection>> sources) where T : notnull => sources.Combine(CombineOperator.Or); /// - /// Apply a logical Or operator between the collections. - /// Items which are in any of the sources are included in the result. + /// Applies a logical OR (union) between the source and other list changeset streams. + /// Items present in any of the sources are included in the result, using reference-counted equality. /// /// The type of the item. - /// The source. - /// The others. - /// An observable which emits the change set. + /// The primary source to union. + /// The other changeset streams to combine with. + /// A list changeset stream containing items that exist in at least one source. + /// is . + /// + /// + /// Uses reference-counted equality comparison. An item is included when it first appears in any source and removed when it no longer exists in any source. + /// Moved changes are ignored by the set logic. + /// + /// + /// EventBehavior + /// Add/AddRange (any source)If the item is new to the result, an Add is emitted. Otherwise the reference count is incremented. + /// Remove/RemoveRange/Clear (any source)Reference count decremented. If count reaches zero, a Remove is emitted. + /// ReplaceOld item reference count decremented, new item reference count incremented. Add/Remove emitted as needed. + /// RefreshForwarded if the item is in the result set. + /// MovedIgnored. + /// OnErrorForwarded from any source. + /// OnCompletedForwarded when all sources have completed. + /// + /// + /// + /// + /// + /// public static IObservable> Or(this IObservable> source, params IObservable>[] others) where T : notnull { @@ -1321,43 +1763,46 @@ public static IObservable> Or(this IObservable> s return source.Combine(CombineOperator.Or, others); } + /// /// - /// Dynamically apply a logical Or operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result. + /// Dynamic OR: sources can be added or removed from the at runtime. /// - /// The type of the item. - /// The source. - /// An observable which emits the change set. public static IObservable> Or(this IObservableList>> sources) where T : notnull => sources.Combine(CombineOperator.Or); + /// /// - /// Dynamically apply a logical Or operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result. + /// Dynamic OR accepting of . Each inner list's Connect() is used as a source. /// - /// The type of the item. - /// The source. - /// An observable which emits the change set. public static IObservable> Or(this IObservableList> sources) where T : notnull => sources.Combine(CombineOperator.Or); + /// /// - /// Dynamically apply a logical Or operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result. + /// Dynamic OR accepting of . Each inner list's Connect() is used as a source. /// - /// The type of the item. - /// The source. - /// An observable which emits the change set. public static IObservable> Or(this IObservableList> sources) where T : notnull => sources.Combine(CombineOperator.Or); /// - /// Applies paging to the data source. + /// Applies page-based windowing to the source list. Only items within the current page (determined by page number and page size from ) are included downstream. /// /// The type of the item. - /// The source. - /// Observable to control page requests. - /// An observable which emits the change set. + /// The source to page. + /// An observable of controlling which page to display (page number and page size). + /// An stream containing only items within the current page window. + /// or is . + /// + /// + /// Maintains the full source list internally and calculates the page window on each change or page request. + /// Items entering the page window produce Add; items leaving produce Remove. A new page request triggers + /// a full recalculation of the page contents. + /// + /// Worth noting: The source should ideally be sorted before paging, as list order determines page contents. Duplicate items are removed from the result via Distinct(). + /// + /// + /// + /// public static IObservable> Page(this IObservable> source, IObservable requests) where T : notnull { @@ -1368,17 +1813,18 @@ public static IObservable> Page(this IObservable - /// list. + /// Subscribes to the source changeset stream and pipes all changes into the . /// /// The type of the object. - /// The source. - /// The destination. - /// An observable which emits the change set. - /// - /// source - /// or - /// destination. - /// + /// The source to pipe into a target list. + /// The destination to receive all changes. + /// An representing the subscription. Dispose to stop piping changes. + /// or is . + /// + /// Each changeset is applied to the destination using Clone() inside an Edit() call, producing a single batch update per changeset. + /// + /// + /// public static IDisposable PopulateInto(this IObservable> source, ISourceList destination) where T : notnull { @@ -1389,18 +1835,21 @@ public static IDisposable PopulateInto(this IObservable> source } /// - /// The latest copy of the cache is exposed for querying after each modification to the underlying data. + /// Emits a projected value from the current list snapshot after every changeset. + /// The receives an representing the current state. /// - /// The type of the object. - /// The type of the destination. - /// The source. - /// The result selector. - /// An observable which emits the destination value. - /// - /// source - /// or - /// resultSelector. - /// + /// The type of items in the list. + /// The type of the projected result. + /// The source to project on each change. + /// A function projecting the current list snapshot to a result value. + /// An observable emitting the projected value after each changeset. + /// or is . + /// + /// Delegates to and applies via Select. + /// + /// + /// + /// public static IObservable QueryWhenChanged(this IObservable> source, Func, TDestination> resultSelector) where TObject : notnull { @@ -1411,12 +1860,26 @@ public static IObservable QueryWhenChanged( } /// - /// The latest copy of the cache is exposed for querying i) after each modification to the underlying data ii) upon subscription. + /// Emits an snapshot of the current list state after every changeset. + /// Maintains an internal list updated by cloning each changeset. /// - /// The type of the object. - /// The source. - /// An observable which emits the read only collection. - /// source. + /// The type of items in the list. + /// The source to project on each change. + /// An observable emitting the full list snapshot as after each change. + /// is . + /// + /// This is a non-changeset operator. It emits the entire collection state on each change, not incremental diffs. + /// + /// EventBehavior + /// Add/AddRange/Replace/Remove/RemoveRange/Moved/Refresh/ClearThe internal list is updated, then the full snapshot is emitted. + /// OnErrorForwarded. + /// OnCompletedForwarded. + /// + /// Worth noting: A new snapshot is emitted on every changeset, which can be chatty. The collection is rebuilt by cloning each changeset into an internal list. For sorted output, use . + /// + /// + /// + /// public static IObservable> QueryWhenChanged(this IObservable> source) where T : notnull { @@ -1426,11 +1889,17 @@ public static IObservable> QueryWhenChanged(this IObse } /// - /// List equivalent to Publish().RefCount(). The source is cached so long as there is at least 1 subscriber. + /// Reference-counted materialization of the source changeset stream into an . + /// The shared list is created on the first subscriber and disposed when the last subscriber unsubscribes. /// /// The type of the item. - /// The source. - /// An observable which emits the change set. + /// The source to share via reference counting. + /// A list changeset stream backed by a shared, reference-counted . + /// is . + /// + /// Equivalent to Publish().RefCount() for changeset streams. The underlying list is created lazily on first subscription. + /// + /// public static IObservable> RefCount(this IObservable> source) where T : notnull { @@ -1440,12 +1909,16 @@ public static IObservable> RefCount(this IObservable - /// Removes the index from all changes. - /// NB: This operator has been introduced as a temporary fix for creating an Or operator using merge many. + /// Strips index information from all changes in the stream. /// /// The type of the object. - /// The source. - /// An observable which emits the change set. + /// The source to strip index information. + /// A list changeset stream with all index values removed from changes. + /// is . + /// + /// Removes index positions from every change in each changeset. This is useful when downstream operators do not require or support index-based operations. + /// + /// public static IObservable> RemoveIndex(this IObservable> source) where T : notnull { @@ -1455,16 +1928,16 @@ public static IObservable> RemoveIndex(this IObservable - /// Reverse sort of the change set. + /// Reverses the order of items in the changeset stream by transforming all indices: new_index = length - old_index - 1. /// /// The type of the item. - /// The source. - /// An observable which emits the change set. - /// - /// source - /// or - /// comparer. - /// + /// The source to reverse. + /// A list changeset stream with all index positions reversed. + /// is . + /// + /// This is a pure index transformation. The items themselves are unchanged; only their positional indices are inverted. + /// + /// public static IObservable> Reverse(this IObservable> source) where T : notnull { @@ -1475,12 +1948,15 @@ public static IObservable> Reverse(this IObservable - /// Defer the subscription until loaded and skip initial change set. + /// Skips the initial changeset (the snapshot emitted on subscription) and forwards all subsequent changesets. + /// Internally defers until loaded, then skips the first emission. /// /// The type of the object. - /// The source. - /// An observable which emits the change set. - /// source. + /// The source to skip the initial changeset. + /// A list changeset stream that omits the initial snapshot. + /// is . + /// + /// public static IObservable> SkipInitial(this IObservable> source) where T : notnull { @@ -1490,19 +1966,39 @@ public static IObservable> SkipInitial(this IObservable - /// Sorts the sequence using the specified comparer. + /// Sorts the list using the specified comparer, maintaining a sorted output that incrementally updates as items change. /// /// The type of the item. - /// The source. - /// The comparer used for sorting. - /// For improved performance, specify SortOptions.UseBinarySearch. This can only be used when the values which are sorted on are immutable. - /// OnNext of this observable causes data to resort. This is required when the value which is sorted on mutable. - /// An observable comparer used to change the comparer on which the sorted list i. - /// Since sorting can be slow for large record sets, the reset threshold is used to force the list re-ordered. - /// An observable which emits the change set. - /// source - /// or - /// comparer. + /// The source to sort. + /// The used for sorting. + /// The for improved performance when sorted values are immutable. + /// An optional of that forces a full re-sort when it fires. Required when sorted property values are mutable. + /// An optional of that replaces the comparer, triggering a full re-sort. + /// When the number of changes exceeds this threshold, a full reset is performed instead of incremental updates. Default is 50. + /// A list changeset stream with items in sorted order. + /// or is . + /// + /// + /// Maintains an internal sorted list. Each incoming change is applied incrementally: adds are inserted at the correct sorted position, + /// removes are removed by index, and refreshes re-evaluate position (emitting Moved if changed). + /// + /// + /// EventBehavior + /// Add/AddRangeInserted at the correct sorted position. May trigger a full reset if the count exceeds . + /// ReplaceOld item removed, new item inserted at sorted position. + /// Remove/RemoveRange/ClearRemoved from sorted list. + /// RefreshSort position re-evaluated. If position changed, a Moved is emitted. + /// Comparer changedFull re-sort of all items. + /// Resort signalFull re-sort using the current comparer. + /// OnErrorForwarded to the downstream observer. + /// OnCompletedForwarded to the downstream observer. + /// + /// Worth noting: is faster but requires that the values being sorted on never mutate. If they do, use the signal or . + /// + /// + /// + /// + /// public static IObservable> Sort(this IObservable> source, IComparer comparer, SortOptions options = SortOptions.None, IObservable? resort = null, IObservable>? comparerChanged = null, int resetThreshold = 50) where T : notnull { @@ -1512,19 +2008,15 @@ public static IObservable> Sort(this IObservable> return new Sort(source, comparer, options, resort, comparerChanged, resetThreshold).Run(); } + /// /// - /// Sorts the sequence using the specified observable comparer. + /// Sorts the list using an observable comparer. The initial comparer is taken from the first emission; subsequent emissions trigger a full re-sort. /// - /// The type of the item. - /// The source. - /// An observable comparer used to change the comparer on which the sorted list i. - /// For improved performance, specify SortOptions.UseBinarySearch. This can only be used when the values which are sorted on are immutable. - /// OnNext of this observable causes data to resort. This is required when the value which is sorted on mutable. - /// Since sorting can be slow for large record sets, the reset threshold is used to force the list re-ordered. - /// An observable which emits the change set. - /// source - /// or - /// comparer. + /// The source to sort. + /// An of that emits comparers. The first emission provides the initial sort order; subsequent emissions trigger re-sorts. + /// for controlling sort behavior. + /// An optional of to force a re-sort with the current comparer. + /// The threshold for triggering a full reset instead of incremental updates. public static IObservable> Sort(this IObservable> source, IObservable> comparerChanged, SortOptions options = SortOptions.None, IObservable? resort = null, int resetThreshold = 50) where T : notnull { @@ -1535,27 +2027,40 @@ public static IObservable> Sort(this IObservable> } /// - /// Prepends an empty change set to the source. + /// Prepends an empty changeset to the source stream. Useful for initializing downstream consumers that expect an initial emission. /// /// The type of item. - /// The source observable of change set values. - /// An observable which emits a change set. + /// The source to prepend an empty changeset to. + /// A list changeset stream that begins with an empty changeset. + /// + /// public static IObservable> StartWithEmpty(this IObservable> source) where T : notnull => source.StartWith(ChangeSet.Empty); /// - /// Subscribes to each item when it is added to the stream and unsubscribes when it is removed. All items will be unsubscribed when the stream is disposed. + /// Creates an subscription for each item via when it is added. + /// The subscription is disposed when the item is removed or replaced. All subscriptions are disposed when the stream terminates. + /// The changeset is forwarded downstream unmodified. /// /// The type of the object. - /// The source. - /// The subscription function. - /// An observable which emits the change set. - /// source - /// or - /// subscriptionFactory. + /// The source to create a subscription for each item in. + /// A function that creates an for each item. + /// A continuation of the source changeset stream with per-item subscriptions managed as a side effect. + /// or is . /// - /// Subscribes to each item when it is added or updates and unsubscribes when it is removed. + /// + /// EventBehavior + /// Add/AddRangeSubscription created for each item via the factory. Changeset forwarded. + /// ReplaceOld item's subscription disposed, new subscription created. Changeset forwarded. + /// Remove/RemoveRange/ClearSubscriptions for removed items are disposed. Changeset forwarded. + /// Moved/RefreshForwarded. No subscription changes. + /// OnError/OnCompleted/DisposalAll active subscriptions are disposed. + /// /// + /// + /// + /// + /// public static IObservable> SubscribeMany(this IObservable> source, Func subscriptionFactory) where T : notnull { @@ -1566,26 +2071,27 @@ public static IObservable> SubscribeMany(this IObservable - /// Suppress refresh notifications. + /// Suppresses all changes from the stream. All other change reasons pass through. /// /// The type of the object. - /// The source observable change set. - /// An observable which emits the change set. + /// The source to strip refresh events. + /// A list changeset stream with Refresh changes removed. + /// + /// public static IObservable> SuppressRefresh(this IObservable> source) where T : notnull => source.WhereReasonsAreNot(ListChangeReason.Refresh); /// - /// Transforms an observable sequence of observable lists into a single sequence - /// producing values only from the most recent observable sequence. - /// Each time a new inner observable sequence is received, unsubscribe from the - /// previous inner observable sequence and clear the existing result set. + /// Subscribes to the latest inner , switching to each new source and clearing the result when switching. /// /// The type of the object. - /// The source. - /// - /// The observable sequence that at any point in time produces the elements of the most recent inner observable sequence that has been received. - /// - /// is null. + /// An observable that emits instances. Each emission triggers a switch to the new list. + /// A list changeset stream reflecting the most recently received inner list. + /// is . + /// + /// Convenience overload that calls Connect() on each inner list, then delegates to . + /// + /// public static IObservable> Switch(this IObservable> sources) where T : notnull { @@ -1595,17 +2101,20 @@ public static IObservable> Switch(this IObservable - /// Transforms an observable sequence of observable changes sets into an observable sequence - /// producing values only from the most recent observable sequence. - /// Each time a new inner observable sequence is received, unsubscribe from the - /// previous inner observable sequence and clear the existing result set. + /// Subscribes to the latest inner changeset stream, switching to each new source and clearing the destination when switching. + /// Previous subscriptions are disposed and the result set is emptied before subscribing to the new inner stream. /// /// The type of the object. - /// The source. - /// - /// The observable sequence that at any point in time produces the elements of the most recent inner observable sequence that has been received. - /// - /// is null. + /// An of changeset streams. The operator subscribes to the latest inner stream. + /// A list changeset stream reflecting the most recently received inner changeset stream. + /// is . + /// + /// + /// On each new inner stream, the operator clears the destination, disposes the previous subscription, and subscribes to the new stream. + /// This is the changeset-aware equivalent of Rx's Switch(). + /// + /// + /// public static IObservable> Switch(this IObservable>> sources) where T : notnull { @@ -1615,23 +2124,35 @@ public static IObservable> Switch(this IObservable - /// Converts the change set into a fully formed collection. Each change in the source results in a new collection. + /// Emits the full collection as an after every changeset. Equivalent to QueryWhenChanged(items => items). /// /// The type of the object. - /// The source. - /// An observable which emits the read only collection. + /// The source to materialize into a collection on each change. + /// An observable emitting the full collection snapshot after each change. + /// + /// public static IObservable> ToCollection(this IObservable> source) where TObject : notnull => source.QueryWhenChanged(items => items); /// - /// Converts the observable to an observable change set. - /// Change set observes observable change events. + /// Bridges an into the DynamicData world by converting each emitted item into a list changeset. + /// Each emission becomes an Add operation in the resulting changeset stream. /// /// The type of the object. - /// The source. - /// The scheduler (only used for time expiry). - /// An observable which emits a change set. - /// source. + /// The source to convert into a changeset stream. + /// An optional for time-based operations (expiry, size limiting). + /// A list changeset stream where each source emission is an Add. + /// is . + /// + /// + /// This is the primary bridge from standard Rx into DynamicData's list changeset model. Each item emitted by + /// is added to an internal list and an Add changeset is emitted. The list grows unboundedly unless size or time limits + /// are specified via other overloads. + /// + /// Worth noting: Source completion and errors are propagated. The internal list is disposed on unsubscribe. + /// + /// + /// public static IObservable> ToObservableChangeSet( this IObservable source, IScheduler? scheduler = null) @@ -1642,16 +2163,14 @@ public static IObservable> ToObservableChangeSet( limitSizeTo: -1, scheduler: scheduler); + /// /// - /// Converts the observable to an observable change set, allowing time expiry to be specified. - /// Change set observes observable change events. + /// Bridges an into a list changeset stream with per-item time-based expiry. + /// Expired items are automatically removed. /// - /// The type of the object. - /// The source. - /// Specify on a per object level the maximum time before an object expires from a cache. - /// The scheduler (only used for time expiry). - /// An observable which emits a change set. - /// source. + /// The source to convert into a changeset stream. + /// A function returning the time-to-live for each item. Return for non-expiring items. + /// An optional for expiry timers. public static IObservable> ToObservableChangeSet( this IObservable source, Func expireAfter, @@ -1663,16 +2182,14 @@ public static IObservable> ToObservableChangeSet( limitSizeTo: -1, scheduler: scheduler); + /// /// - /// Converts the observable to an observable change set, with a specified limit of how large the list can be. - /// Change set observes observable change events. + /// Bridges an into a list changeset stream with FIFO size limiting. + /// When the list exceeds , the oldest items are removed. /// - /// The type of the object. - /// The source. - /// Remove the oldest items when the size has reached this limit. Supply -1 to disable size limiting. - /// The scheduler (only used for time expiry). - /// An observable which emits a change set. - /// source. + /// The source to convert into a changeset stream. + /// The maximum list size. Supply -1 to disable size limiting. + /// An optional for scheduling removals. public static IObservable> ToObservableChangeSet( this IObservable source, int limitSizeTo, @@ -1684,17 +2201,14 @@ public static IObservable> ToObservableChangeSet( limitSizeTo: limitSizeTo, scheduler: scheduler); + /// /// - /// Converts the observable to an observable change set, allowing size and time limit to be specified. - /// Change set observes observable change events. + /// Bridges an into a list changeset stream with both time-based expiry and FIFO size limiting. /// - /// The type of the object. - /// The source. - /// Specify on a per object level the maximum time before an object expires from a cache. - /// Remove the oldest items when the size has reached this limit. Supply -1 to disable size limiting. - /// The scheduler (only used for time expiry). - /// An observable which emits a change set. - /// source. + /// The source to convert into a changeset stream. + /// A function returning the time-to-live for each item. Return for non-expiring items. + /// The maximum list size. Supply -1 to disable size limiting. + /// An optional for expiry timers and size-limit checks. public static IObservable> ToObservableChangeSet( this IObservable source, Func? expireAfter, @@ -1707,15 +2221,13 @@ public static IObservable> ToObservableChangeSet( limitSizeTo: limitSizeTo, scheduler: scheduler); + /// /// - /// Converts the observable to an observable change set. - /// Change set observes observable change events. + /// Bridges an of batches into a list changeset stream. + /// Each emitted batch becomes an AddRange. /// - /// The type of the object. - /// The source. - /// The scheduler (only used for time expiry). - /// An observable which emits a change set. - /// source. + /// The source of to convert into a changeset stream. + /// An optional for time-based operations. public static IObservable> ToObservableChangeSet( this IObservable> source, IScheduler? scheduler = null) @@ -1726,16 +2238,13 @@ public static IObservable> ToObservableChangeSet( limitSizeTo: -1, scheduler: scheduler); + /// /// - /// Converts the observable to an observable change set, allowing size and time limit to be specified. - /// Change set observes observable change events. + /// Bridges an of batches into a list changeset stream with FIFO size limiting. /// - /// The type of the object. - /// The source. - /// Remove the oldest items when the size has reached this limit. - /// The scheduler (only used for time expiry). - /// An observable which emits a change set. - /// source. + /// The source of to convert into a changeset stream. + /// The maximum list size. Oldest items are removed when the limit is exceeded. + /// An optional for scheduling removals. public static IObservable> ToObservableChangeSet( this IObservable> source, int limitSizeTo, @@ -1747,16 +2256,13 @@ public static IObservable> ToObservableChangeSet( limitSizeTo: limitSizeTo, scheduler: scheduler); + /// /// - /// Converts the observable to an observable change set, allowing size to be specified. - /// Change set observes observable change events. + /// Bridges an of batches into a list changeset stream with time-based expiry. /// - /// The type of the object. - /// The source. - /// Specify on a per object level the maximum time before an object expires from a cache. - /// The scheduler (only used for time expiry). - /// An observable which emits a change set. - /// source. + /// The source of to convert into a changeset stream. + /// A function returning the time-to-live for each item. Return for non-expiring items. + /// An optional for expiry timers. public static IObservable> ToObservableChangeSet( this IObservable> source, Func expireAfter, @@ -1768,19 +2274,14 @@ public static IObservable> ToObservableChangeSet( limitSizeTo: -1, scheduler: scheduler); + /// /// - /// Converts the observable to an observable change set, allowing size and time limit to be specified. - /// Change set observes observable change events. + /// Bridges an of batches into a list changeset stream with both time-based expiry and FIFO size limiting. /// - /// The type of the object. - /// The source. - /// Specify on a per object level the maximum time before an object expires from a cache. - /// Remove the oldest items when the size has reached this limit. - /// The scheduler (only used for time expiry). - /// An observable which emits a change set. - /// source - /// or - /// keySelector. + /// The source of to convert into a changeset stream. + /// A function returning the time-to-live for each item. Return for non-expiring items. + /// The maximum list size. Oldest items removed when exceeded. + /// An optional for expiry timers and size-limit checks. public static IObservable> ToObservableChangeSet( this IObservable> source, Func? expireAfter, @@ -1794,12 +2295,20 @@ public static IObservable> ToObservableChangeSet( scheduler: scheduler); /// - /// Limits the size of the result set to the specified number of items. + /// Takes the first items from the source list. Implemented as Virtualise with a fixed window starting at index 0. /// /// The type of the item. - /// The source. - /// The number of items. - /// An observable which emits the change set. + /// The source to take the top items. + /// The maximum number of items to include. Must be greater than zero. + /// A virtual changeset stream containing at most items from the beginning of the source. + /// is . + /// is zero or negative. + /// + /// The source should ideally be sorted before applying Top, since list order determines which items appear. + /// + /// + /// + /// public static IObservable> Top(this IObservable> source, int numberOfItems) where T : notnull { @@ -1814,24 +2323,29 @@ public static IObservable> Top(this IObservable> } /// - /// Converts the change set into a fully formed sorted collection. Each change in the source results in a new sorted collection. + /// Emits a sorted after every changeset, sorted by the value returned by . /// /// The type of the object. - /// The sort key. - /// The source. - /// The sort function. - /// The sort order. Defaults to ascending. - /// An observable which emits the read only collection. + /// The type of the sort key. + /// The source to materialize into a sorted collection on each change. + /// A function extracting the sort key from each item. + /// The sort direction. Defaults to ascending. + /// An observable emitting a sorted collection snapshot after each change. + /// + /// + /// public static IObservable> ToSortedCollection(this IObservable> source, Func sort, SortDirection sortOrder = SortDirection.Ascending) where TObject : notnull => source.QueryWhenChanged(query => sortOrder == SortDirection.Ascending ? new ReadOnlyCollectionLight(query.OrderBy(sort)) : new ReadOnlyCollectionLight(query.OrderByDescending(sort))); /// - /// Converts the change set into a fully formed sorted collection. Each change in the source results in a new sorted collection. + /// Emits a sorted after every changeset, sorted using the specified . /// /// The type of the object. - /// The source. - /// The sort comparer. - /// An observable which emits the read only collection. + /// The source to materialize into a sorted collection on each change. + /// The used for sorting. + /// An observable emitting a sorted collection snapshot after each change. + /// + /// public static IObservable> ToSortedCollection(this IObservable> source, IComparer comparer) where TObject : notnull => source.QueryWhenChanged( query => @@ -1842,19 +2356,39 @@ public static IObservable> ToSortedCollection - /// Projects each update item to a new form using the specified transform function. + /// Projects each item to a new form using a synchronous transform function. /// /// The type of the source. /// The type of the destination. - /// The source. - /// The transform factory. - /// Should a new transform be applied when a refresh event is received. - /// An observable which emits the change set. - /// - /// source - /// or - /// valueSelector. - /// + /// The source to transform. + /// The transform function applied to each item. + /// When , Refresh events re-invoke the factory and emit an update. When (the default), Refresh is forwarded without re-transforming. + /// A list changeset stream of transformed items. + /// + /// + /// Maintains an internal list of transformed items. Each source changeset is + /// processed and a corresponding output changeset is produced with the transformed items. + /// + /// + /// EventBehavior + /// AddThe factory is called and an Add is emitted at the same index. + /// AddRangeThe factory is called for each item. An AddRange is emitted at the same start index. + /// ReplaceThe factory is called for the new item. A Replace is emitted at the same index. The previous transformed value is available to overloads that accept . + /// RemoveA Remove is emitted (no factory call). + /// RemoveRangeA RemoveRange is emitted. + /// MovedA Moved is emitted with updated indices (no factory call). Throws if the source change has no index information. + /// RefreshIf is (default), the Refresh is forwarded without re-transforming. If , the factory is re-invoked and the result replaces the current value. + /// ClearA Clear is emitted and the internal list is emptied. + /// OnErrorForwarded. If the factory throws, the exception propagates as OnError. + /// OnCompletedForwarded to the downstream observer. + /// + /// Worth noting: By default, Refresh does NOT re-transform the item (it just forwards the signal). Set to if you need the factory re-invoked on Refresh. Add operations with out-of-bounds indices silently append to the end. + /// + /// or is . + /// + /// + /// + /// public static IObservable> Transform(this IObservable> source, Func transformFactory, bool transformOnRefresh = false) where TSource : notnull where TDestination : notnull @@ -1866,20 +2400,13 @@ public static IObservable> Transform((t, _, _) => transformFactory(t), transformOnRefresh); } + /// /// - /// Projects each update item to a new form using the specified transform function. + /// Projects each item using a transform function that also receives the item's index. /// - /// The type of the source. - /// The type of the destination. - /// The source. - /// The transform function. - /// Should a new transform be applied when a refresh event is received. - /// A an observable change set of the transformed object. - /// - /// source - /// or - /// valueSelector. - /// + /// The source to transform. + /// A function receiving the source item and its index, returning the transformed item. + /// When , Refresh events re-invoke the factory. public static IObservable> Transform(this IObservable> source, Func transformFactory, bool transformOnRefresh = false) where TSource : notnull where TDestination : notnull @@ -1890,21 +2417,14 @@ public static IObservable> Transform((t, _, idx) => transformFactory(t, idx), transformOnRefresh); } + /// /// - /// Projects each update item to a new form using the specified transform function. - /// *** Annoyingly when using this overload you will have to explicitly specify the generic type arguments as type inference fails. + /// Projects each item using a transform function that also receives the previously transformed value (if any). + /// Type arguments must be specified explicitly as type inference fails for this overload. /// - /// The type of the source. - /// The type of the destination. - /// The source. - /// The transform function. - /// Should a new transform be applied when a refresh event is received. - /// A an observable change set of the transformed object. - /// - /// source - /// or - /// valueSelector. - /// + /// The source to transform. + /// A function receiving the source item and the previous transformed value (as ), returning the new transformed item. + /// When , Refresh events re-invoke the factory. public static IObservable> Transform(this IObservable> source, Func, TDestination> transformFactory, bool transformOnRefresh = false) where TSource : notnull where TDestination : notnull @@ -1915,21 +2435,14 @@ public static IObservable> Transform((t, previous, _) => transformFactory(t, previous), transformOnRefresh); } + /// /// - /// Projects each update item to a new form using the specified transform function. - /// *** Annoyingly when using this overload you will have to explicitly specify the generic type arguments as type inference fails. + /// Projects each item using a transform function that receives the source item, the previously transformed value, and the index. + /// Type arguments must be specified explicitly as type inference fails for this overload. /// - /// The type of the source. - /// The type of the destination. - /// The source. - /// The transform factory. - /// Should a new transform be applied when a refresh event is received. - /// A an observable change set of the transformed object. - /// - /// source - /// or - /// valueSelector. - /// + /// The source to transform. + /// A function receiving the source item, previous transformed value, and index. + /// When , Refresh events re-invoke the factory. public static IObservable> Transform(this IObservable> source, Func, int, TDestination> transformFactory, bool transformOnRefresh = false) where TSource : notnull where TDestination : notnull @@ -1941,19 +2454,32 @@ public static IObservable> Transform - /// Projects each update item to a new form using the specified transform function. + /// Projects each item to a new form using an async transform function. Behaves like but the factory returns a . /// /// The type of the source. /// The type of the destination. - /// The source. - /// The transform factory. - /// Should a new transform be applied when a refresh event is received. - /// A an observable change set of the transformed object. - /// - /// source - /// or - /// valueSelector. - /// + /// The source to transform asynchronously. + /// An async function that transforms each source item. + /// When , Refresh events re-invoke the factory. + /// A list changeset stream of asynchronously transformed items. + /// or is . + /// + /// Change handling is identical to the synchronous except the factory is awaited. Operations are serialized per changeset via a semaphore. + /// + /// EventBehavior + /// Add/AddRangeThe async factory is awaited for each item. An Add/AddRange is emitted with the transformed results. + /// ReplaceThe async factory is awaited for the new item. A Replace is emitted. + /// Remove/RemoveRangeEmitted without invoking the factory. + /// MovedEmitted with updated indices (no factory call). + /// RefreshIf is (default), forwarded without re-transforming. If , the factory is re-awaited. + /// ClearEmitted and internal list cleared. + /// OnErrorForwarded. If the async factory throws, the exception propagates as OnError. + /// OnCompletedForwarded after the last changeset is processed. + /// + /// Worth noting: All async transforms within a single changeset are serialized (not parallel). Each changeset is fully processed before the next begins. By default, Refresh does NOT re-transform. + /// + /// + /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Roslynator", "RCS1047:Non-asynchronous method name should not end with 'Async'.", Justification = "By Design.")] public static IObservable> TransformAsync( this IObservable> source, @@ -1968,20 +2494,10 @@ public static IObservable> TransformAsync((t, _, _) => transformFactory(t), transformOnRefresh); } + /// /// - /// Projects each update item to a new form using the specified transform function. + /// Async transform overload receiving the source item and its index. /// - /// The type of the source. - /// The type of the destination. - /// The source. - /// The transform factory. - /// Should a new transform be applied when a refresh event is received. - /// A an observable change set of the transformed object. - /// - /// source - /// or - /// valueSelector. - /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Roslynator", "RCS1047:Non-asynchronous method name should not end with 'Async'.", Justification = "By Design.")] public static IObservable> TransformAsync( this IObservable> source, @@ -1996,20 +2512,10 @@ public static IObservable> TransformAsync((t, _, i) => transformFactory(t, i), transformOnRefresh); } + /// /// - /// Projects each update item to a new form using the specified transform function. + /// Async transform overload receiving the source item and the previously transformed value. /// - /// The type of the source. - /// The type of the destination. - /// The source. - /// The transform factory. - /// Should a new transform be applied when a refresh event is received. - /// A an observable change set of the transformed object. - /// - /// source - /// or - /// valueSelector. - /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Roslynator", "RCS1047:Non-asynchronous method name should not end with 'Async'.", Justification = "By Design.")] public static IObservable> TransformAsync( this IObservable> source, @@ -2024,20 +2530,10 @@ public static IObservable> TransformAsync((t, d, _) => transformFactory(t, d), transformOnRefresh); } + /// /// - /// Projects each update item to a new form using the specified transform function. + /// Async transform overload receiving the source item, previously transformed value, and index. This is the terminal overload that all other TransformAsync overloads delegate to. /// - /// The type of the source. - /// The type of the destination. - /// The source. - /// The transform factory. - /// Should a new transform be applied when a refresh event is received. - /// A an observable change set of the transformed object. - /// - /// source - /// or - /// valueSelector. - /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Roslynator", "RCS1047:Non-asynchronous method name should not end with 'Async'.", Justification = "By Design.")] public static IObservable> TransformAsync( this IObservable> source, @@ -2053,19 +2549,30 @@ public static IObservable> TransformAsync - /// Equivalent to a select many transform. To work, the key must individually identify each child. + /// Flattens each source item into multiple destination items using . Each source item produces zero or more children, + /// all of which are merged into a single flat list changeset stream. /// - /// The type of the destination. - /// The type of the source. - /// The source. - /// The selector function which selects the enumerable. - /// Used when an item has been replaced to determine whether child items are the same as previous children. - /// An observable which emits the change set. - /// - /// source - /// or - /// manySelector. - /// + /// The type of the destination items. + /// The type of the source items. + /// The source to expand each item into multiple children. + /// A function that returns the child items for each source item. + /// An optional used during Replace to determine which child items changed between old and new parent values. + /// A list changeset stream of all child items from all source items. + /// or is . + /// + /// + /// EventBehavior + /// Add/AddRangeChildren expanded and added to the output. + /// ReplaceOld children diffed against new children (using ). Removed, added, or kept as appropriate. + /// Remove/RemoveRange/ClearAll children of the removed parents are removed from the output. + /// RefreshChildren re-expanded and diffed. + /// OnErrorForwarded to the downstream observer. + /// OnCompletedForwarded to the downstream observer. + /// + /// + /// + /// + /// public static IObservable> TransformMany(this IObservable> source, Func> manySelector, IEqualityComparer? equalityComparer = null) where TDestination : notnull where TSource : notnull @@ -2076,52 +2583,47 @@ public static IObservable> TransformMany(source, manySelector, equalityComparer).Run(); } + /// /// - /// Flatten the nested observable collection, and observe subsequently observable collection changes. + /// Flattens each source item into children from an . The collection is observed for subsequent changes. /// - /// The type of the destination. - /// The type of the source. - /// The source. - /// The selector function which selects the enumerable. - /// Used when an item has been replaced to determine whether child items are the same as previous children. - /// An observable which emits the change set. public static IObservable> TransformMany(this IObservable> source, Func> manySelector, IEqualityComparer? equalityComparer = null) where TDestination : notnull where TSource : notnull => new TransformMany(source, manySelector, equalityComparer).Run(); + /// /// - /// Flatten the nested observable collection, and observe subsequently observable collection changes. + /// Flattens each source item into children from a . The collection is observed for subsequent changes. /// - /// The type of the destination. - /// The type of the source. - /// The source. - /// The selector function which selects the enumerable. - /// Used when an item has been replaced to determine whether child items are the same as previous children. - /// An observable which emits the change set. public static IObservable> TransformMany(this IObservable> source, Func> manySelector, IEqualityComparer? equalityComparer = null) where TDestination : notnull where TSource : notnull => new TransformMany(source, manySelector, equalityComparer).Run(); + /// /// - /// Flatten the nested observable list, and observe subsequent observable collection changes. + /// Flattens each source item into children from an . The inner list is observed for subsequent changes. /// - /// The type of the destination. - /// The type of the source. - /// The source. - /// The selector function which selects the enumerable. - /// Used when an item has been replaced to determine whether child items are the same as previous children. - /// An observable which emits the change set. public static IObservable> TransformMany(this IObservable> source, Func> manySelector, IEqualityComparer? equalityComparer = null) where TDestination : notnull where TSource : notnull => new TransformMany(source, manySelector, equalityComparer).Run(); /// - /// Virtualises the source using parameters provided via the requests observable. + /// Applies a sliding window to the source list using start index and size from . + /// Only items within the window are included downstream. /// /// The type of the item. - /// The source. - /// The requests. - /// An observable which emits the change set. + /// The source to virtualize. + /// An observable of specifying the start index and size of the window. + /// An stream containing only items within the current virtual window. + /// or is . + /// + /// + /// Like but uses absolute start index and size instead of page number and page size. + /// Internally maintains the full source list and recalculates the window on each change or request. + /// + /// + /// + /// public static IObservable> Virtualise(this IObservable> source, IObservable requests) where T : notnull { @@ -2133,12 +2635,22 @@ public static IObservable> Virtualise(this IObservable - /// Watches each item in the collection and notifies when any of them has changed. + /// Watches all items in the source list and emits the item when any of its properties change. + /// Requires to implement . + /// This is NOT a changeset operator: it returns a flat . /// - /// The type of the object. - /// The source. - /// specify properties to Monitor, or omit to monitor all property changes. - /// An observable which emits the object. + /// The type of the object. Must implement . + /// The source to observe property changes on items in. + /// An optional list of property names to monitor. If empty, all property changes are observed. + /// An observable emitting the item whenever any monitored property changes. + /// is . + /// + /// Implemented via . Subscriptions are managed per item: created on add, disposed on remove. + /// + /// + /// + /// + /// public static IObservable WhenAnyPropertyChanged(this IObservable> source, params string[] propertiesToMonitor) where TObject : INotifyPropertyChanged { @@ -2148,14 +2660,23 @@ public static IObservable> Virtualise(this IObservable - /// Watches each item in the collection and notifies when any of them has changed. + /// Watches a specific property on all items in the source list and emits a (item + value pair) when it changes. + /// Requires to implement . + /// This is NOT a changeset operator: it returns a flat . /// - /// The type of object. - /// The type of the value. - /// The source. - /// The property accessor. - /// If true the resulting observable includes the initial value. - /// An observable which emits the property value. + /// The type of item. Must implement . + /// The type of the property value. + /// The source to observe a specific property on items in. + /// An expression selecting the property to observe. + /// When (default), the current value is emitted immediately upon subscribing to each item. + /// An observable emitting whenever the property changes on any tracked item. + /// or is . + /// + /// Implemented via . + /// + /// + /// + /// public static IObservable> WhenPropertyChanged(this IObservable> source, Expression> propertyAccessor, bool notifyOnInitialValue = true) where TObject : INotifyPropertyChanged { @@ -2167,14 +2688,20 @@ public static IObservable> WhenPropertyChanged - /// Watches each item in the collection and notifies when any of them has changed. + /// Watches a specific property on all items and emits just the property value (without the sender) when it changes. + /// Requires to implement . + /// This is NOT a changeset operator: it returns a flat . /// - /// The type of object. - /// The type of the value. - /// The source. - /// The property accessor. - /// If true the resulting observable includes the initial value. - /// An observable which emits the value. + /// The type of item. Must implement . + /// The type of the property value. + /// The source to observe a specific property value on items in. + /// An expression selecting the property to observe. + /// When (default), the current value is emitted immediately upon subscribing to each item. + /// An observable emitting the property value whenever it changes on any tracked item. + /// or is . + /// + /// + /// public static IObservable WhenValueChanged(this IObservable> source, Expression> propertyAccessor, bool notifyOnInitialValue = true) where TObject : INotifyPropertyChanged { @@ -2186,13 +2713,29 @@ public static IObservable> WhenPropertyChanged - /// Includes changes for the specified reasons only. + /// Filters the changeset stream to include only changes with the specified values. + /// Index information is stripped from the output because removing some changes invalidates the original index positions. /// /// The type of the item. - /// The source. - /// The reasons. - /// An observable which emits the change set. - /// Must enter at least 1 reason. + /// The source to filter by change reason. + /// The change reasons to include. Must specify at least one. + /// A list changeset stream containing only changes with the specified reasons. + /// is . + /// is empty. + /// + /// Filters individual changes within each changeset. If filtering removes all changes from a changeset, the empty changeset is suppressed via . + /// + /// EventBehavior + /// Any matching reasonThe change is included in the output. Index information is stripped. + /// Any non-matching reasonThe change is dropped from the output. + /// OnErrorForwarded. + /// OnCompletedForwarded. + /// + /// Worth noting: Filtering out Remove changes can cause downstream operators to accumulate items indefinitely (memory leak). Index information is stripped because removing some changes invalidates the original index positions. + /// + /// + /// + /// public static IObservable> WhereReasonsAre(this IObservable> source, params ListChangeReason[] reasons) where T : notnull { @@ -2213,13 +2756,25 @@ public static IObservable> WhereReasonsAre(this IObservable - /// Excludes updates for the specified reasons. + /// Filters the changeset stream to exclude changes with the specified values. + /// Index information is stripped from the output because removing some changes invalidates the original index positions. + /// The exception is when only is excluded, since removing Refresh does not affect index calculations. /// /// The type of the item. - /// The source. - /// The reasons. - /// An observable which emits the change set. - /// Must enter at least 1 reason. + /// The source to filter by excluding change reasons. + /// The change reasons to exclude. Must specify at least one. + /// A list changeset stream with the specified change reasons removed. + /// is . + /// is empty. + /// + /// + /// Empty changesets (after filtering) are automatically suppressed. When only is excluded, + /// indices are preserved, since removing Refresh does not affect index calculations. + /// + /// + /// + /// + /// public static IObservable> WhereReasonsAreNot(this IObservable> source, params ListChangeReason[] reasons) where T : notnull { @@ -2250,13 +2805,35 @@ public static IObservable> WhereReasonsAreNot(this IObservable< } /// - /// Apply a logical Xor operator between the collections. - /// Items which are only in one of the sources are included in the result. + /// Applies a logical XOR (symmetric difference) between the source and other streams. + /// Items present in exactly one source are included in the result. /// /// The type of the item. - /// The source. - /// The others. - /// An observable which emits the change set. + /// The primary source to exclusively combine. + /// The other changeset streams to combine with. + /// A list changeset stream containing items that exist in exactly one source. + /// is . + /// + /// + /// Uses reference-counted equality. An item is included when it exists in exactly one source. + /// If it appears in a second source, it is removed from the result. If it then leaves one source, + /// it re-enters the result. Moved changes are ignored. + /// + /// + /// EventBehavior + /// Add/AddRangeReference count updated. If the item is now in exactly one source, an Add is emitted. If now in two or more, a Remove is emitted. + /// Remove/RemoveRange/ClearReference count decremented. If now in exactly one source, an Add is emitted. If now in zero, a Remove is emitted. + /// ReplaceOld item reference count decremented, new item incremented, with Xor logic applied. + /// RefreshForwarded if item is in the result set. + /// MovedIgnored. + /// OnErrorForwarded from any source. + /// OnCompletedForwarded when all sources have completed. + /// + /// + /// + /// + /// + /// public static IObservable> Xor(this IObservable> source, params IObservable>[] others) where T : notnull { @@ -2265,43 +2842,31 @@ public static IObservable> Xor(this IObservable> return source.Combine(CombineOperator.Xor, others); } + /// /// - /// Apply a logical Xor operator between the collections. - /// Items which are only in one of the sources are included in the result. + /// Applies a logical XOR between a pre-built collection of list changeset sources. /// - /// The type of the item. - /// The sources. - /// An observable which emits the change set. public static IObservable> Xor(this ICollection>> sources) where T : notnull => sources.Combine(CombineOperator.Xor); + /// /// - /// Dynamically apply a logical Xor operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result. + /// Dynamic XOR: sources can be added or removed from the at runtime. /// - /// The type of the item. - /// The source. - /// An observable which emits the change set. public static IObservable> Xor(this IObservableList>> sources) where T : notnull => sources.Combine(CombineOperator.Xor); + /// /// - /// Dynamically apply a logical Xor operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result. + /// Dynamic XOR accepting of . Each inner list's Connect() is used as a source. /// - /// The type of the item. - /// The source. - /// An observable which emits the change set. public static IObservable> Xor(this IObservableList> sources) where T : notnull => sources.Combine(CombineOperator.Xor); + /// /// - /// Dynamically apply a logical Xor operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result. + /// Dynamic XOR accepting of . Each inner list's Connect() is used as a source. /// - /// The type of the item. - /// The source. - /// An observable which emits the change set. public static IObservable> Xor(this IObservableList> sources) where T : notnull => sources.Combine(CombineOperator.Xor);