Dict, HashSet, Queue, RingBuffer, Stack

This commit is contained in:
neuecc 2024-08-27 18:36:01 +09:00
parent ce624265f3
commit 295cef5ae5
7 changed files with 418 additions and 248 deletions

View File

@ -9,7 +9,7 @@ namespace ObservableCollections
{
public sealed partial class ObservableDictionary<TKey, TValue>
{
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, bool _ = false)
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform)
{
// reverse is no used.
return new View<TView>(this, transform);
@ -19,32 +19,45 @@ namespace ObservableCollections
{
readonly ObservableDictionary<TKey, TValue> source;
readonly Func<KeyValuePair<TKey, TValue>, TView> selector;
ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter;
ISynchronizedViewFilter<KeyValuePair<TKey, TValue>> filter;
readonly Dictionary<TKey, (TValue, TView)> dict;
int filteredCount;
public View(ObservableDictionary<TKey, TValue> source, Func<KeyValuePair<TKey, TValue>, TView> selector)
{
this.source = source;
this.selector = selector;
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView>.Null;
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
this.dict = source.dictionary.ToDictionary(x => x.Key, x => (x.Value, selector(x)));
this.filteredCount = dict.Count;
this.source.CollectionChanged += SourceCollectionChanged;
}
}
public object SyncRoot { get; }
public event NotifyCollectionChangedEventHandler<KeyValuePair<TKey, TValue>>? RoutingCollectionChanged;
public event Action<SynchronizedViewChangedEventArgs<KeyValuePair<TKey, TValue>, TView>>? ViewChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> CurrentFilter
public ISynchronizedViewFilter<KeyValuePair<TKey, TValue>> Filter
{
get { lock (SyncRoot) return filter; }
}
public int Count
{
get
{
lock (SyncRoot)
{
return filteredCount;
}
}
}
public int UnfilteredCount
{
get
{
@ -60,76 +73,104 @@ namespace ObservableCollections
this.source.CollectionChanged -= SourceCollectionChanged;
}
public void AttachFilter(ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter, bool invokeAddEventForCurrentElements = false)
public void AttachFilter(ISynchronizedViewFilter<KeyValuePair<TKey, TValue>> filter)
{
if (filter.IsNullFilter())
{
ResetFilter();
return;
}
lock (SyncRoot)
{
this.filter = filter;
this.filteredCount = 0;
foreach (var v in dict)
{
var value = new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1);
var view = v.Value.Item2;
if (invokeAddEventForCurrentElements)
if (filter.IsMatch(value))
{
filter.InvokeOnAdd(value, view, -1);
}
else
{
filter.InvokeOnAttach(value, view);
filteredCount++;
}
}
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<KeyValuePair<TKey, TValue>, TView>(NotifyViewChangedAction.FilterReset));
}
}
public void ResetFilter(Action<KeyValuePair<TKey, TValue>, TView>? resetAction)
public void ResetFilter()
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView>.Null;
if (resetAction != null)
{
foreach (var v in dict)
{
resetAction(new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1), v.Value.Item2);
}
}
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>>.Null;
this.filteredCount = dict.Count;
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<KeyValuePair<TKey, TValue>, TView>(NotifyViewChangedAction.FilterReset));
}
}
public ISynchronizedViewList<TView> ToViewList()
{
return new SynchronizedViewList<KeyValuePair<TKey, TValue>, TView>(this);
}
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
{
lock (SyncRoot)
{
return new NotifyCollectionChangedSynchronizedView<KeyValuePair<TKey, TValue>, TView>(this, null);
}
return new NotifyCollectionChangedSynchronizedView<KeyValuePair<TKey, TValue>, TView>(this, null);
}
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
{
lock (SyncRoot)
{
return new NotifyCollectionChangedSynchronizedView<KeyValuePair<TKey, TValue>, TView>(this, collectionEventDispatcher);
}
return new NotifyCollectionChangedSynchronizedView<KeyValuePair<TKey, TValue>, TView>(this, collectionEventDispatcher);
}
public IEnumerator<(KeyValuePair<TKey, TValue>, TView)> GetEnumerator()
public IEnumerator<TView> GetEnumerator()
{
lock (SyncRoot)
{
foreach (var item in dict)
{
var v = (new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2);
if (filter.IsMatch(v.Item1, v.Item2))
if (filter.IsMatch(v.Item1))
{
yield return v;
yield return v.Item2;
}
}
}
}
IEnumerator IEnumerable.GetEnumerator()
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerable<(KeyValuePair<TKey, TValue> Value, TView View)> Filtered
{
return GetEnumerator();
get
{
lock (SyncRoot)
{
foreach (var item in dict)
{
var v = (new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2);
if (filter.IsMatch(v.Item1))
{
yield return v;
}
}
}
}
}
public IEnumerable<(KeyValuePair<TKey, TValue> Value, TView View)> Unfiltered
{
get
{
lock (SyncRoot)
{
foreach (var item in dict)
{
var v = (new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2);
yield return v;
}
}
}
}
private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>> e)
@ -140,41 +181,40 @@ namespace ObservableCollections
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
{
var v = selector(e.NewItem);
dict.Add(e.NewItem.Key, (e.NewItem.Value, v));
filter.InvokeOnAdd(e.NewItem, v, -1);
}
{
var v = selector(e.NewItem);
dict.Add(e.NewItem.Key, (e.NewItem.Value, v));
this.InvokeOnAdd(ref filteredCount, ViewChanged, e.NewItem, v, -1);
}
break;
case NotifyCollectionChangedAction.Remove:
{
if (dict.Remove(e.OldItem.Key, out var v))
{
filter.InvokeOnRemove(e.OldItem, v.Item2, -1);
if (dict.Remove(e.OldItem.Key, out var v))
{
this.InvokeOnRemove(ref filteredCount, ViewChanged, e.OldItem, v.Item2, -1);
}
}
}
break;
case NotifyCollectionChangedAction.Replace:
{
var v = selector(e.NewItem);
dict.Remove(e.OldItem.Key, out var ov);
dict[e.NewItem.Key] = (e.NewItem.Value, v);
{
var v = selector(e.NewItem);
dict.Remove(e.OldItem.Key, out var ov);
dict[e.NewItem.Key] = (e.NewItem.Value, v);
filter.InvokeOnReplace(e.NewItem, v, e.OldItem, ov.Item2, -1);
}
this.InvokeOnReplace(ref filteredCount, ViewChanged, e.NewItem, v, e.OldItem, ov.Item2, -1);
}
break;
case NotifyCollectionChangedAction.Reset:
{
dict.Clear();
filter.InvokeOnReset();
}
{
dict.Clear();
this.InvokeOnReset(ref filteredCount, ViewChanged);
}
break;
case NotifyCollectionChangedAction.Move: // ObservableDictionary have no Move operation.
default:
break;
}
RoutingCollectionChanged?.Invoke(e);
CollectionStateChanged?.Invoke(e.Action);
}
}

View File

@ -7,8 +7,7 @@ using System.Linq;
namespace ObservableCollections
{
public sealed partial class ObservableDictionary<TKey, TValue> : IDictionary<TKey, TValue>,
IReadOnlyObservableDictionary<TKey, TValue>
public sealed partial class ObservableDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IReadOnlyObservableDictionary<TKey, TValue>
where TKey : notnull
{
readonly Dictionary<TKey, TValue> dictionary;

View File

@ -4,19 +4,20 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace ObservableCollections
{
public sealed partial class ObservableHashSet<T> : IReadOnlyCollection<T>, IObservableCollection<T>
{
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool _ = false)
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform)
{
return new View<TView>(this, transform);
}
sealed class View<TView> : ISynchronizedView<T, TView>
{
public ISynchronizedViewFilter<T, TView> CurrentFilter
public ISynchronizedViewFilter<T> Filter
{
get { lock (SyncRoot) return filter; }
}
@ -24,10 +25,11 @@ namespace ObservableCollections
readonly ObservableHashSet<T> source;
readonly Func<T, TView> selector;
readonly Dictionary<T, (T, TView)> dict;
int filteredCount;
ISynchronizedViewFilter<T, TView> filter;
ISynchronizedViewFilter<T> filter;
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
public event Action<SynchronizedViewChangedEventArgs<T, TView>>? ViewChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public object SyncRoot { get; }
@ -36,16 +38,28 @@ namespace ObservableCollections
{
this.source = source;
this.selector = selector;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.filter = SynchronizedViewFilter<T>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
this.dict = source.set.ToDictionary(x => x, x => (x, selector(x)));
this.filteredCount = dict.Count;
this.source.CollectionChanged += SourceCollectionChanged;
}
}
public int Count
{
get
{
lock (SyncRoot)
{
return filteredCount;
}
}
}
public int UnfilteredCount
{
get
{
@ -56,65 +70,62 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
public void AttachFilter(ISynchronizedViewFilter<T> filter)
{
if (filter.IsNullFilter())
{
ResetFilter();
return;
}
lock (SyncRoot)
{
this.filter = filter;
this.filteredCount = 0;
foreach (var (_, (value, view)) in dict)
{
if (invokeAddEventForCurrentElements)
if (filter.IsMatch(value))
{
filter.InvokeOnAdd((value, view), -1);
}
else
{
filter.InvokeOnAttach(value, view);
filteredCount++;
}
}
}
}
public void ResetFilter(Action<T, TView>? resetAction)
public void ResetFilter()
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T, TView>.Null;
if (resetAction != null)
{
foreach (var (_, (value, view)) in dict)
{
resetAction(value, view);
}
}
this.filter = SynchronizedViewFilter<T>.Null;
this.filteredCount = dict.Count;
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
}
}
public ISynchronizedViewList<TView> ToViewList()
{
return new SynchronizedViewList<T, TView>(this);
}
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
{
lock (SyncRoot)
{
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, null);
}
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, null);
}
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
{
lock (SyncRoot)
{
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, collectionEventDispatcher);
}
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, collectionEventDispatcher);
}
public IEnumerator<(T, TView)> GetEnumerator()
public IEnumerator<TView> GetEnumerator()
{
lock (SyncRoot)
{
foreach (var item in dict)
{
if (filter.IsMatch(item.Value.Item1, item.Value.Item2))
if (filter.IsMatch(item.Value.Item1))
{
yield return item.Value;
yield return item.Value.Item2;
}
}
}
@ -122,6 +133,37 @@ namespace ObservableCollections
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerable<(T Value, TView View)> Filtered
{
get
{
lock (SyncRoot)
{
foreach (var item in dict)
{
if (filter.IsMatch(item.Value.Item1))
{
yield return item.Value;
}
}
}
}
}
public IEnumerable<(T Value, TView View)> Unfiltered
{
get
{
lock (SyncRoot)
{
foreach (var item in dict)
{
yield return item.Value;
}
}
}
}
public void Dispose()
{
this.source.CollectionChanged -= SourceCollectionChanged;
@ -138,7 +180,7 @@ namespace ObservableCollections
{
var v = (e.NewItem, selector(e.NewItem));
dict.Add(e.NewItem, v);
filter.InvokeOnAdd(v, -1);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, -1);
}
else
{
@ -147,7 +189,7 @@ namespace ObservableCollections
{
var v = (item, selector(item));
dict.Add(item, v);
filter.InvokeOnAdd(v, i++);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, i++);
}
}
break;
@ -156,7 +198,7 @@ namespace ObservableCollections
{
if (dict.Remove(e.OldItem, out var value))
{
filter.InvokeOnRemove(value, -1);
this.InvokeOnRemove(ref filteredCount, ViewChanged, value, -1);
}
}
else
@ -165,14 +207,14 @@ namespace ObservableCollections
{
if (dict.Remove(item, out var value))
{
filter.InvokeOnRemove(value, -1);
this.InvokeOnRemove(ref filteredCount, ViewChanged, value, -1);
}
}
}
break;
case NotifyCollectionChangedAction.Reset:
dict.Clear();
filter.InvokeOnReset();
this.InvokeOnReset(ref filteredCount, ViewChanged);
break;
case NotifyCollectionChangedAction.Replace:
case NotifyCollectionChangedAction.Move:
@ -180,7 +222,6 @@ namespace ObservableCollections
break;
}
RoutingCollectionChanged?.Invoke(e);
CollectionStateChanged?.Invoke(e.Action);
}
}

View File

@ -98,7 +98,6 @@ namespace ObservableCollections
lock (SyncRoot)
{
this.filter = filter;
this.filteredCount = 0;
for (var i = 0; i < list.Count; i++)
{

View File

@ -4,14 +4,15 @@ using System.Collections.Specialized;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ObservableCollections
{
public sealed partial class ObservableQueue<T> : IReadOnlyCollection<T>, IObservableCollection<T>
{
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform)
{
return new View<TView>(this, transform, reverse);
return new View<TView>(this, transform);
}
class View<TView> : ISynchronizedView<T, TView>
@ -20,34 +21,46 @@ namespace ObservableCollections
readonly Func<T, TView> selector;
readonly bool reverse;
protected readonly Queue<(T, TView)> queue;
int filteredCount;
ISynchronizedViewFilter<T, TView> filter;
ISynchronizedViewFilter<T> filter;
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
public event Action<SynchronizedViewChangedEventArgs<T, TView>>? ViewChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public object SyncRoot { get; }
public ISynchronizedViewFilter<T, TView> CurrentFilter
public ISynchronizedViewFilter<T> Filter
{
get { lock (SyncRoot) return filter; }
}
public View(ObservableQueue<T> source, Func<T, TView> selector, bool reverse)
public View(ObservableQueue<T> source, Func<T, TView> selector)
{
this.source = source;
this.selector = selector;
this.reverse = reverse;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.filter = SynchronizedViewFilter<T>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
this.queue = new Queue<(T, TView)>(source.queue.Select(x => (x, selector(x))));
this.filteredCount = queue.Count;
this.source.CollectionChanged += SourceCollectionChanged;
}
}
public int Count
{
get
{
lock (SyncRoot)
{
return filteredCount;
}
}
}
public int UnfilteredCount
{
get
{
@ -58,79 +71,63 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
public void AttachFilter(ISynchronizedViewFilter<T> filter)
{
if (filter.IsNullFilter())
{
ResetFilter();
return;
}
lock (SyncRoot)
{
this.filter = filter;
var i = 0;
this.filteredCount = 0;
foreach (var (value, view) in queue)
{
if (invokeAddEventForCurrentElements)
if (filter.IsMatch(value))
{
filter.InvokeOnAdd(value, view, i++);
}
else
{
filter.InvokeOnAttach(value, view);
filteredCount++;
}
}
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
}
}
public void ResetFilter(Action<T, TView>? resetAction)
public void ResetFilter()
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T, TView>.Null;
if (resetAction != null)
{
foreach (var (item, view) in queue)
{
resetAction(item, view);
}
}
this.filter = SynchronizedViewFilter<T>.Null;
this.filteredCount = queue.Count;
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
}
}
public ISynchronizedViewList<TView> ToViewList()
{
return new SynchronizedViewList<T, TView>(this);
}
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
{
lock (SyncRoot)
{
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, null);
}
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, null);
}
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
{
lock (SyncRoot)
{
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, collectionEventDispatcher);
}
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, collectionEventDispatcher);
}
public IEnumerator<(T, TView)> GetEnumerator()
public IEnumerator<TView> GetEnumerator()
{
lock (SyncRoot)
{
if (!reverse)
foreach (var item in queue)
{
foreach (var item in queue)
if (filter.IsMatch(item.Item1))
{
if (filter.IsMatch(item.Item1, item.Item2))
{
yield return item;
}
}
}
else
{
foreach (var item in queue.AsEnumerable().Reverse())
{
if (filter.IsMatch(item.Item1, item.Item2))
{
yield return item;
}
yield return item.Item2;
}
}
}
@ -138,6 +135,37 @@ namespace ObservableCollections
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerable<(T Value, TView View)> Filtered
{
get
{
lock (SyncRoot)
{
foreach (var item in queue)
{
if (filter.IsMatch(item.Item1))
{
yield return item;
}
}
}
}
}
public IEnumerable<(T Value, TView View)> Unfiltered
{
get
{
lock (SyncRoot)
{
foreach (var item in queue)
{
yield return item;
}
}
}
}
public void Dispose()
{
this.source.CollectionChanged -= SourceCollectionChanged;
@ -155,7 +183,7 @@ namespace ObservableCollections
{
var v = (e.NewItem, selector(e.NewItem));
queue.Enqueue(v);
filter.InvokeOnAdd(v, e.NewStartingIndex);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, e.NewStartingIndex);
}
else
{
@ -164,7 +192,7 @@ namespace ObservableCollections
{
var v = (item, selector(item));
queue.Enqueue(v);
filter.InvokeOnAdd(v, i++);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, i++);
}
}
break;
@ -173,7 +201,7 @@ namespace ObservableCollections
if (e.IsSingleItem)
{
var v = queue.Dequeue();
filter.InvokeOnRemove(v.Item1, v.Item2, 0);
this.InvokeOnRemove(ref filteredCount, ViewChanged, v.Item1, v.Item2, 0);
}
else
{
@ -181,13 +209,13 @@ namespace ObservableCollections
for (int i = 0; i < len; i++)
{
var v = queue.Dequeue();
filter.InvokeOnRemove(v.Item1, v.Item2, 0);
this.InvokeOnRemove(ref filteredCount, ViewChanged, v.Item1, v.Item2, 0);
}
}
break;
case NotifyCollectionChangedAction.Reset:
queue.Clear();
filter.InvokeOnReset();
this.InvokeOnReset(ref filteredCount, ViewChanged);
break;
case NotifyCollectionChangedAction.Replace:
case NotifyCollectionChangedAction.Move:
@ -195,7 +223,6 @@ namespace ObservableCollections
break;
}
RoutingCollectionChanged?.Invoke(e);
CollectionStateChanged?.Invoke(e.Action);
}
}

View File

@ -4,51 +4,63 @@ using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
namespace ObservableCollections
{
public sealed partial class ObservableRingBuffer<T>
{
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform)
{
return new View<TView>(this, transform, reverse);
return new View<TView>(this, transform);
}
// used with ObservableFixedSizeRingBuffer
internal sealed class View<TView> : ISynchronizedView<T, TView>
{
public ISynchronizedViewFilter<T, TView> CurrentFilter
public ISynchronizedViewFilter<T> Filter
{
get { lock (SyncRoot) return filter; }
}
readonly IObservableCollection<T> source;
readonly Func<T, TView> selector;
readonly bool reverse;
readonly RingBuffer<(T, TView)> ringBuffer;
int filteredCount;
ISynchronizedViewFilter<T, TView> filter;
ISynchronizedViewFilter<T> filter;
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
public event Action<SynchronizedViewChangedEventArgs<T, TView>>? ViewChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public object SyncRoot { get; }
public View(IObservableCollection<T> source, Func<T, TView> selector, bool reverse)
public View(IObservableCollection<T> source, Func<T, TView> selector)
{
this.source = source;
this.selector = selector;
this.reverse = reverse;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.filter = SynchronizedViewFilter<T>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
this.ringBuffer = new RingBuffer<(T, TView)>(source.Select(x => (x, selector(x))));
this.filteredCount = ringBuffer.Count;
this.source.CollectionChanged += SourceCollectionChanged;
}
}
public int Count
{
get
{
lock (SyncRoot)
{
return filteredCount;
}
}
}
public int UnfilteredCount
{
get
{
@ -59,41 +71,44 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
public void AttachFilter(ISynchronizedViewFilter<T> filter)
{
if (filter.IsNullFilter())
{
ResetFilter();
return;
}
lock (SyncRoot)
{
this.filter = filter;
this.filteredCount = 0;
for (var i = 0; i < ringBuffer.Count; i++)
{
var (value, view) = ringBuffer[i];
if (invokeAddEventForCurrentElements)
if (filter.IsMatch(value))
{
filter.InvokeOnAdd(value, view, i);
}
else
{
filter.InvokeOnAttach(value, view);
filteredCount++;
}
}
}
}
public void ResetFilter(Action<T, TView>? resetAction)
public void ResetFilter()
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T, TView>.Null;
if (resetAction != null)
{
foreach (var (item, view) in ringBuffer)
{
resetAction(item, view);
}
}
this.filter = SynchronizedViewFilter<T>.Null;
this.filteredCount = ringBuffer.Count;
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
}
}
public ISynchronizedViewList<TView> ToViewList()
{
return new SynchronizedViewList<T, TView>(this);
}
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
{
lock (SyncRoot)
@ -110,25 +125,31 @@ namespace ObservableCollections
}
}
public IEnumerator<(T, TView)> GetEnumerator()
public IEnumerator<TView> GetEnumerator()
{
lock (SyncRoot)
{
if (!reverse)
foreach (var item in ringBuffer)
{
if (filter.IsMatch(item.Item1))
{
yield return item.Item2;
}
}
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerable<(T Value, TView View)> Filtered
{
get
{
lock (SyncRoot)
{
foreach (var item in ringBuffer)
{
if (filter.IsMatch(item.Item1, item.Item2))
{
yield return item;
}
}
}
else
{
foreach (var item in ringBuffer.AsEnumerable().Reverse())
{
if (filter.IsMatch(item.Item1, item.Item2))
if (filter.IsMatch(item.Item1))
{
yield return item;
}
@ -137,7 +158,19 @@ namespace ObservableCollections
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerable<(T Value, TView View)> Unfiltered
{
get
{
lock (SyncRoot)
{
foreach (var item in ringBuffer)
{
yield return item;
}
}
}
}
public void Dispose()
{
@ -162,7 +195,7 @@ namespace ObservableCollections
{
var v = (e.NewItem, selector(e.NewItem));
ringBuffer.AddFirst(v);
filter.InvokeOnAdd(v, 0);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, 0);
}
else
{
@ -170,7 +203,7 @@ namespace ObservableCollections
{
var v = (item, selector(item));
ringBuffer.AddFirst(v);
filter.InvokeOnAdd(v, 0);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, 0);
}
}
}
@ -181,7 +214,7 @@ namespace ObservableCollections
{
var v = (e.NewItem, selector(e.NewItem));
ringBuffer.AddLast(v);
filter.InvokeOnAdd(v, ringBuffer.Count - 1);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, ringBuffer.Count - 1);
}
else
{
@ -189,7 +222,7 @@ namespace ObservableCollections
{
var v = (item, selector(item));
ringBuffer.AddLast(v);
filter.InvokeOnAdd(v, ringBuffer.Count - 1);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, ringBuffer.Count - 1);
}
}
}
@ -202,14 +235,14 @@ namespace ObservableCollections
if (e.IsSingleItem)
{
var v = ringBuffer.RemoveFirst();
filter.InvokeOnRemove(v, 0);
this.InvokeOnRemove(ref filteredCount, ViewChanged, v, 0);
}
else
{
for (int i = 0; i < e.OldItems.Length; i++)
{
var v = ringBuffer.RemoveFirst();
filter.InvokeOnRemove(v, 0);
this.InvokeOnRemove(ref filteredCount, ViewChanged, v, 0);
}
}
}
@ -220,7 +253,7 @@ namespace ObservableCollections
{
var index = ringBuffer.Count - 1;
var v = ringBuffer.RemoveLast();
filter.InvokeOnRemove(v, index);
this.InvokeOnRemove(ref filteredCount, ViewChanged, v, index);
}
else
{
@ -228,14 +261,14 @@ namespace ObservableCollections
{
var index = ringBuffer.Count - 1;
var v = ringBuffer.RemoveLast();
filter.InvokeOnRemove(v, index);
this.InvokeOnRemove(ref filteredCount, ViewChanged, v, index);
}
}
}
break;
case NotifyCollectionChangedAction.Reset:
ringBuffer.Clear();
filter.InvokeOnReset();
this.InvokeOnReset(ref filteredCount, ViewChanged);
break;
case NotifyCollectionChangedAction.Replace:
// range is not supported
@ -243,7 +276,7 @@ namespace ObservableCollections
var ov = ringBuffer[e.OldStartingIndex];
var v = (e.NewItem, selector(e.NewItem));
ringBuffer[e.NewStartingIndex] = v;
filter.InvokeOnReplace(v, ov, e.NewStartingIndex);
this.InvokeOnReplace(ref filteredCount, ViewChanged, v, ov, e.NewStartingIndex);
break;
}
case NotifyCollectionChangedAction.Move:
@ -251,7 +284,6 @@ namespace ObservableCollections
break;
}
RoutingCollectionChanged?.Invoke(e);
CollectionStateChanged?.Invoke(e.Action);
}
}

View File

@ -9,45 +9,56 @@ namespace ObservableCollections
{
public sealed partial class ObservableStack<T> : IReadOnlyCollection<T>, IObservableCollection<T>
{
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform)
{
return new View<TView>(this, transform, reverse);
return new View<TView>(this, transform);
}
class View<TView> : ISynchronizedView<T, TView>
{
readonly ObservableStack<T> source;
readonly Func<T, TView> selector;
readonly bool reverse;
protected readonly Stack<(T, TView)> stack;
int filteredCount;
ISynchronizedViewFilter<T, TView> filter;
ISynchronizedViewFilter<T> filter;
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
public event Action<SynchronizedViewChangedEventArgs<T, TView>>? ViewChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public object SyncRoot { get; }
public ISynchronizedViewFilter<T, TView> CurrentFilter
public ISynchronizedViewFilter<T> Filter
{
get { lock (SyncRoot) return filter; }
}
public View(ObservableStack<T> source, Func<T, TView> selector, bool reverse)
public View(ObservableStack<T> source, Func<T, TView> selector)
{
this.source = source;
this.selector = selector;
this.reverse = reverse;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.filter = SynchronizedViewFilter<T>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
this.stack = new Stack<(T, TView)>(source.stack.Select(x => (x, selector(x))));
this.filteredCount = stack.Count;
this.source.CollectionChanged += SourceCollectionChanged;
}
}
public int Count
{
get
{
lock (SyncRoot)
{
return filteredCount;
}
}
}
public int UnfilteredCount
{
get
{
@ -58,40 +69,44 @@ namespace ObservableCollections
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
public void AttachFilter(ISynchronizedViewFilter<T> filter)
{
if (filter.IsNullFilter())
{
ResetFilter();
return;
}
lock (SyncRoot)
{
this.filter = filter;
this.filteredCount = 0;
foreach (var (value, view) in stack)
{
if (invokeAddEventForCurrentElements)
if (filter.IsMatch(value))
{
filter.InvokeOnAdd(value, view, 0);
}
else
{
filter.InvokeOnAttach(value, view);
filteredCount++;
}
}
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
}
}
public void ResetFilter(Action<T, TView>? resetAction)
public void ResetFilter()
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T, TView>.Null;
if (resetAction != null)
{
foreach (var (item, view) in stack)
{
resetAction(item, view);
}
}
this.filter = SynchronizedViewFilter<T>.Null;
this.filteredCount = stack.Count;
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
}
}
public ISynchronizedViewList<TView> ToViewList()
{
return new SynchronizedViewList<T, TView>(this);
}
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
{
lock (SyncRoot)
@ -108,25 +123,31 @@ namespace ObservableCollections
}
}
public IEnumerator<(T, TView)> GetEnumerator()
public IEnumerator<TView> GetEnumerator()
{
lock (SyncRoot)
{
if (!reverse)
foreach (var item in stack)
{
if (filter.IsMatch(item.Item1))
{
yield return item.Item2;
}
}
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerable<(T Value, TView View)> Filtered
{
get
{
lock (SyncRoot)
{
foreach (var item in stack)
{
if (filter.IsMatch(item.Item1, item.Item2))
{
yield return item;
}
}
}
else
{
foreach (var item in stack.AsEnumerable().Reverse())
{
if (filter.IsMatch(item.Item1, item.Item2))
if (filter.IsMatch(item.Item1))
{
yield return item;
}
@ -135,7 +156,19 @@ namespace ObservableCollections
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerable<(T Value, TView View)> Unfiltered
{
get
{
lock (SyncRoot)
{
foreach (var item in stack)
{
yield return item;
}
}
}
}
public void Dispose()
{
@ -154,7 +187,7 @@ namespace ObservableCollections
{
var v = (e.NewItem, selector(e.NewItem));
stack.Push(v);
filter.InvokeOnAdd(v, 0);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, 0);
}
else
{
@ -162,7 +195,7 @@ namespace ObservableCollections
{
var v = (item, selector(item));
stack.Push(v);
filter.InvokeOnAdd(v, 0);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, 0);
}
}
break;
@ -171,7 +204,7 @@ namespace ObservableCollections
if (e.IsSingleItem)
{
var v = stack.Pop();
filter.InvokeOnRemove(v.Item1, v.Item2, 0);
this.InvokeOnRemove(ref filteredCount, ViewChanged, v.Item1, v.Item2, 0);
}
else
{
@ -179,13 +212,13 @@ namespace ObservableCollections
for (int i = 0; i < len; i++)
{
var v = stack.Pop();
filter.InvokeOnRemove(v.Item1, v.Item2, 0);
this.InvokeOnRemove(ref filteredCount, ViewChanged, v.Item1, v.Item2, 0);
}
}
break;
case NotifyCollectionChangedAction.Reset:
stack.Clear();
filter.InvokeOnReset();
this.InvokeOnReset(ref filteredCount, ViewChanged);
break;
case NotifyCollectionChangedAction.Replace:
case NotifyCollectionChangedAction.Move:
@ -193,7 +226,6 @@ namespace ObservableCollections
break;
}
RoutingCollectionChanged?.Invoke(e);
CollectionStateChanged?.Invoke(e.Action);
}
}