breaking changes, ISynchronizedView<T> -> ISynchronizedView<T, TView>

This commit is contained in:
neuecc 2024-10-18 21:07:20 +09:00
parent bd7cc350f2
commit 73c5b8e83e
10 changed files with 92 additions and 68 deletions

View File

@ -35,7 +35,7 @@ namespace ObservableCollections
public interface ISynchronizedView<T, TView> : IReadOnlyCollection<TView>, IDisposable
{
object SyncRoot { get; }
ISynchronizedViewFilter<T> Filter { get; }
ISynchronizedViewFilter<T, TView> Filter { get; }
IEnumerable<(T Value, TView View)> Filtered { get; }
IEnumerable<(T Value, TView View)> Unfiltered { get; }
int UnfilteredCount { get; }
@ -44,7 +44,7 @@ namespace ObservableCollections
event Action<RejectedViewChangedAction, int, int>? RejectedViewChanged;
event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
void AttachFilter(ISynchronizedViewFilter<T> filter);
void AttachFilter(ISynchronizedViewFilter<T, TView> filter);
void ResetFilter();
ISynchronizedViewList<TView> ToViewList();
NotifyCollectionChangedSynchronizedViewList<TView> ToNotifyCollectionChanged();

View File

@ -1,24 +1,38 @@
using System;
using System.Collections.Specialized;
namespace ObservableCollections
{
// Obsolete...
[Obsolete("this interface is obsoleted. Use ISynchronizedViewFilter<T, TView> instead.")]
public interface ISynchronizedViewFilter<T>
{
bool IsMatch(T value);
}
public class SynchronizedViewFilter<T>(Func<T, bool> isMatch) : ISynchronizedViewFilter<T>
public interface ISynchronizedViewFilter<T, TView>
{
public static readonly ISynchronizedViewFilter<T> Null = new NullViewFilter();
bool IsMatch(T value, TView view);
}
public bool IsMatch(T value) => isMatch(value);
internal class SynchronizedViewValueOnlyFilter<T, TView>(Func<T, bool> isMatch) : ISynchronizedViewFilter<T, TView>
{
public bool IsMatch(T value, TView view) => isMatch(value);
class NullViewFilter : ISynchronizedViewFilter<T>
class NullViewFilter : ISynchronizedViewFilter<T, TView>
{
public bool IsMatch(T value) => true;
public bool IsMatch(T value, TView view) => true;
}
}
public class SynchronizedViewFilter<T, TView>(Func<T, TView, bool> isMatch) : ISynchronizedViewFilter<T, TView>
{
public static readonly ISynchronizedViewFilter<T, TView> Null = new NullViewFilter();
public bool IsMatch(T value, TView view) => isMatch(value, view);
class NullViewFilter : ISynchronizedViewFilter<T, TView>
{
public bool IsMatch(T value, TView view) => true;
}
}
}

View File

@ -19,7 +19,7 @@ namespace ObservableCollections
{
readonly ObservableDictionary<TKey, TValue> source;
readonly Func<KeyValuePair<TKey, TValue>, TView> selector;
ISynchronizedViewFilter<KeyValuePair<TKey, TValue>> filter;
ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter;
readonly Dictionary<TKey, (TValue, TView)> dict;
int filteredCount;
@ -27,7 +27,7 @@ namespace ObservableCollections
{
this.source = source;
this.selector = selector;
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>>.Null;
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
@ -42,7 +42,7 @@ namespace ObservableCollections
public event Action<RejectedViewChangedAction, int, int>? RejectedViewChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public ISynchronizedViewFilter<KeyValuePair<TKey, TValue>> Filter
public ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> Filter
{
get { lock (SyncRoot) return filter; }
}
@ -74,7 +74,7 @@ namespace ObservableCollections
this.source.CollectionChanged -= SourceCollectionChanged;
}
public void AttachFilter(ISynchronizedViewFilter<KeyValuePair<TKey, TValue>> filter)
public void AttachFilter(ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter)
{
if (filter.IsNullFilter())
{
@ -89,7 +89,7 @@ namespace ObservableCollections
foreach (var v in dict)
{
var value = new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1);
if (filter.IsMatch(value))
if (filter.IsMatch(value, v.Value.Item2))
{
filteredCount++;
}
@ -103,7 +103,7 @@ namespace ObservableCollections
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>>.Null;
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView>.Null;
this.filteredCount = dict.Count;
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<KeyValuePair<TKey, TValue>, TView>(NotifyCollectionChangedAction.Reset, true));
}
@ -131,7 +131,7 @@ namespace ObservableCollections
foreach (var item in dict)
{
var v = (new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2);
if (filter.IsMatch(v.Item1))
if (filter.IsMatch(v))
{
yield return v.Item2;
}
@ -150,7 +150,7 @@ namespace ObservableCollections
foreach (var item in dict)
{
var v = (new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2);
if (filter.IsMatch(v.Item1))
if (filter.IsMatch(v))
{
yield return v;
}

View File

@ -17,7 +17,7 @@ namespace ObservableCollections
sealed class View<TView> : ISynchronizedView<T, TView>
{
public ISynchronizedViewFilter<T> Filter
public ISynchronizedViewFilter<T, TView> Filter
{
get { lock (SyncRoot) return filter; }
}
@ -27,7 +27,7 @@ namespace ObservableCollections
readonly Dictionary<T, (T, TView)> dict;
int filteredCount;
ISynchronizedViewFilter<T> filter;
ISynchronizedViewFilter<T, TView> filter;
public event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
public event Action<RejectedViewChangedAction, int, int>? RejectedViewChanged;
@ -39,7 +39,7 @@ namespace ObservableCollections
{
this.source = source;
this.selector = selector;
this.filter = SynchronizedViewFilter<T>.Null;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
@ -71,7 +71,7 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
if (filter.IsNullFilter())
{
@ -85,7 +85,7 @@ namespace ObservableCollections
this.filteredCount = 0;
foreach (var (_, (value, view)) in dict)
{
if (filter.IsMatch(value))
if (filter.IsMatch(value, view))
{
filteredCount++;
}
@ -98,7 +98,7 @@ namespace ObservableCollections
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T>.Null;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.filteredCount = dict.Count;
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset, true));
}
@ -125,7 +125,7 @@ namespace ObservableCollections
{
foreach (var item in dict)
{
if (filter.IsMatch(item.Value.Item1))
if (filter.IsMatch(item.Value))
{
yield return item.Value.Item2;
}
@ -143,7 +143,7 @@ namespace ObservableCollections
{
foreach (var item in dict)
{
if (filter.IsMatch(item.Value.Item1))
if (filter.IsMatch(item.Value))
{
yield return item.Value;
}

View File

@ -48,7 +48,7 @@ namespace ObservableCollections
internal sealed class View<TView> : ISynchronizedView<T, TView>, IWritableSynchronizedView<T, TView>
{
public ISynchronizedViewFilter<T> Filter
public ISynchronizedViewFilter<T, TView> Filter
{
get
{
@ -61,7 +61,7 @@ namespace ObservableCollections
internal readonly List<(T, TView)> list; // unsafe, be careful to use
int filteredCount;
ISynchronizedViewFilter<T> filter;
ISynchronizedViewFilter<T, TView> filter;
public event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
public event Action<RejectedViewChangedAction, int, int>? RejectedViewChanged;
@ -73,7 +73,7 @@ namespace ObservableCollections
{
this.source = source;
this.selector = selector;
this.filter = SynchronizedViewFilter<T>.Null;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
@ -105,7 +105,7 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
if (filter.IsNullFilter())
{
@ -119,7 +119,7 @@ namespace ObservableCollections
this.filteredCount = 0;
for (var i = 0; i < list.Count; i++)
{
if (filter.IsMatch(list[i].Item1))
if (filter.IsMatch(list[i]))
{
filteredCount++;
}
@ -133,7 +133,7 @@ namespace ObservableCollections
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T>.Null;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.filteredCount = list.Count;
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset, true));
}
@ -160,7 +160,7 @@ namespace ObservableCollections
{
foreach (var item in list)
{
if (filter.IsMatch(item.Item1))
if (filter.IsMatch(item))
{
yield return item.Item2;
}
@ -178,7 +178,7 @@ namespace ObservableCollections
{
foreach (var item in list)
{
if (filter.IsMatch(item.Item1))
if (filter.IsMatch(item))
{
yield return item;
}
@ -235,7 +235,7 @@ namespace ObservableCollections
var view = selector(item);
views.Span[i] = view;
valueViews.Span[i] = (item, view);
var isMatch = matches.Span[i] = Filter.IsMatch(item);
var isMatch = matches.Span[i] = Filter.IsMatch(item, view);
if (isMatch)
{
filteredCount++; // increment in this process
@ -271,7 +271,7 @@ namespace ObservableCollections
var item = list[i];
values.Span[j] = item.Item1;
views.Span[j] = item.Item2;
var isMatch = matches.Span[j] = Filter.IsMatch(item.Item1);
var isMatch = matches.Span[j] = Filter.IsMatch(item);
if (isMatch)
{
filteredCount--; // decrement in this process

View File

@ -22,7 +22,7 @@ namespace ObservableCollections
protected readonly Queue<(T, TView)> queue;
int filteredCount;
ISynchronizedViewFilter<T> filter;
ISynchronizedViewFilter<T, TView> filter;
public event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
public event Action<RejectedViewChangedAction, int, int>? RejectedViewChanged;
@ -30,7 +30,7 @@ namespace ObservableCollections
public object SyncRoot { get; }
public ISynchronizedViewFilter<T> Filter
public ISynchronizedViewFilter<T, TView> Filter
{
get { lock (SyncRoot) return filter; }
}
@ -39,7 +39,7 @@ namespace ObservableCollections
{
this.source = source;
this.selector = selector;
this.filter = SynchronizedViewFilter<T>.Null;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
@ -71,7 +71,7 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
if (filter.IsNullFilter())
{
@ -85,7 +85,7 @@ namespace ObservableCollections
this.filteredCount = 0;
foreach (var (value, view) in queue)
{
if (filter.IsMatch(value))
if (filter.IsMatch(value, view))
{
filteredCount++;
}
@ -98,7 +98,7 @@ namespace ObservableCollections
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T>.Null;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.filteredCount = queue.Count;
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset, true));
}
@ -125,7 +125,7 @@ namespace ObservableCollections
{
foreach (var item in queue)
{
if (filter.IsMatch(item.Item1))
if (filter.IsMatch(item))
{
yield return item.Item2;
}
@ -143,7 +143,7 @@ namespace ObservableCollections
{
foreach (var item in queue)
{
if (filter.IsMatch(item.Item1))
if (filter.IsMatch(item))
{
yield return item;
}

View File

@ -18,7 +18,7 @@ namespace ObservableCollections
// used with ObservableFixedSizeRingBuffer
internal sealed class View<TView> : ISynchronizedView<T, TView>
{
public ISynchronizedViewFilter<T> Filter
public ISynchronizedViewFilter<T, TView> Filter
{
get { lock (SyncRoot) return filter; }
}
@ -28,7 +28,7 @@ namespace ObservableCollections
readonly RingBuffer<(T, TView)> ringBuffer;
int filteredCount;
ISynchronizedViewFilter<T> filter;
ISynchronizedViewFilter<T, TView> filter;
public event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
public event Action<RejectedViewChangedAction, int, int>? RejectedViewChanged;
@ -40,7 +40,7 @@ namespace ObservableCollections
{
this.source = source;
this.selector = selector;
this.filter = SynchronizedViewFilter<T>.Null;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
@ -72,7 +72,7 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
if (filter.IsNullFilter())
{
@ -87,7 +87,7 @@ namespace ObservableCollections
for (var i = 0; i < ringBuffer.Count; i++)
{
var (value, view) = ringBuffer[i];
if (filter.IsMatch(value))
if (filter.IsMatch(value, view))
{
filteredCount++;
}
@ -100,7 +100,7 @@ namespace ObservableCollections
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T>.Null;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.filteredCount = ringBuffer.Count;
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset, true));
}
@ -133,7 +133,7 @@ namespace ObservableCollections
{
foreach (var item in ringBuffer)
{
if (filter.IsMatch(item.Item1))
if (filter.IsMatch(item))
{
yield return item.Item2;
}
@ -151,7 +151,7 @@ namespace ObservableCollections
{
foreach (var item in ringBuffer)
{
if (filter.IsMatch(item.Item1))
if (filter.IsMatch(item))
{
yield return item;
}

View File

@ -21,7 +21,7 @@ namespace ObservableCollections
protected readonly Stack<(T, TView)> stack;
int filteredCount;
ISynchronizedViewFilter<T> filter;
ISynchronizedViewFilter<T, TView> filter;
public event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
public event Action<RejectedViewChangedAction, int, int>? RejectedViewChanged;
@ -29,7 +29,7 @@ namespace ObservableCollections
public object SyncRoot { get; }
public ISynchronizedViewFilter<T> Filter
public ISynchronizedViewFilter<T, TView> Filter
{
get { lock (SyncRoot) return filter; }
}
@ -38,7 +38,7 @@ namespace ObservableCollections
{
this.source = source;
this.selector = selector;
this.filter = SynchronizedViewFilter<T>.Null;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
@ -70,7 +70,7 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T> filter)
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
if (filter.IsNullFilter())
{
@ -84,7 +84,7 @@ namespace ObservableCollections
this.filteredCount = 0;
foreach (var (value, view) in stack)
{
if (filter.IsMatch(value))
if (filter.IsMatch(value, view))
{
filteredCount++;
}
@ -97,7 +97,7 @@ namespace ObservableCollections
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T>.Null;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.filteredCount = stack.Count;
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset, true));
}
@ -130,7 +130,7 @@ namespace ObservableCollections
{
foreach (var item in stack)
{
if (filter.IsMatch(item.Item1))
if (filter.IsMatch(item))
{
yield return item.Item2;
}
@ -148,7 +148,7 @@ namespace ObservableCollections
{
foreach (var item in stack)
{
if (filter.IsMatch(item.Item1))
if (filter.IsMatch(item))
{
yield return item;
}

View File

@ -86,12 +86,22 @@ namespace ObservableCollections
{
public static void AttachFilter<T, TView>(this ISynchronizedView<T, TView> source, Func<T, bool> filter)
{
source.AttachFilter(new SynchronizedViewFilter<T>(filter));
source.AttachFilter(new SynchronizedViewValueOnlyFilter<T, TView>(filter));
}
public static bool IsNullFilter<T>(this ISynchronizedViewFilter<T> filter)
public static void AttachFilter<T, TView>(this ISynchronizedView<T, TView> source, Func<T, TView, bool> filter)
{
return filter == SynchronizedViewFilter<T>.Null;
source.AttachFilter(new SynchronizedViewFilter<T, TView>(filter));
}
public static bool IsNullFilter<T, TView>(this ISynchronizedViewFilter<T, TView> filter)
{
return filter == SynchronizedViewFilter<T, TView>.Null;
}
internal static bool IsMatch<T, TView>(this ISynchronizedViewFilter<T, TView> filter, (T, TView) item)
{
return filter.IsMatch(item.Item1, item.Item2);
}
internal static void InvokeOnAdd<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, Action<RejectedViewChangedAction, int, int>? ev2, (T value, TView view) value, int index)
@ -101,7 +111,7 @@ namespace ObservableCollections
internal static void InvokeOnAdd<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, Action<RejectedViewChangedAction, int, int>? ev2, T value, TView view, int index)
{
var isMatch = collection.Filter.IsMatch(value);
var isMatch = collection.Filter.IsMatch(value, view);
if (isMatch)
{
filteredCount++;
@ -144,7 +154,7 @@ namespace ObservableCollections
internal static void InvokeOnRemove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, Action<RejectedViewChangedAction, int, int>? ev2, T value, TView view, int oldIndex)
{
var isMatch = collection.Filter.IsMatch(value);
var isMatch = collection.Filter.IsMatch(value, view);
if (isMatch)
{
filteredCount--;
@ -188,7 +198,7 @@ namespace ObservableCollections
internal static void InvokeOnMove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, Action<RejectedViewChangedAction, int, int>? ev2, T value, TView view, int index, int oldIndex)
{
// move does not changes filtered-count
var isMatch = collection.Filter.IsMatch(value);
var isMatch = collection.Filter.IsMatch(value, view);
if (isMatch)
{
ev?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Move, true, newItem: (value, view), newStartingIndex: index, oldStartingIndex: oldIndex));
@ -206,8 +216,8 @@ namespace ObservableCollections
internal static void InvokeOnReplace<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, T value, TView view, T oldValue, TView oldView, int index, int oldIndex = -1)
{
var oldMatched = collection.Filter.IsMatch(oldValue);
var newMatched = collection.Filter.IsMatch(value);
var oldMatched = collection.Filter.IsMatch(oldValue, oldView);
var newMatched = collection.Filter.IsMatch(value, view);
var bothMatched = oldMatched && newMatched;
if (bothMatched)

View File

@ -54,7 +54,7 @@ internal sealed class FiltableSynchronizedViewList<T, TView> : NotifyCollectionC
{
foreach (var item in parent.Unfiltered) // use Unfiltered
{
if (filter.IsMatch(item.Value))
if (filter.IsMatch(item))
{
yield return (index, item.View);
}