This commit is contained in:
neuecc 2021-08-05 19:06:37 +09:00
parent 327850a0db
commit c7cf88a57d
5 changed files with 413 additions and 204 deletions

View File

@ -0,0 +1,57 @@
using ObservableCollections.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace ObservableCollections
{
public sealed class FreezedDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>, IFreezedCollection<KeyValuePair<TKey, TValue>>
where TKey : notnull
{
readonly IReadOnlyDictionary<TKey, TValue> dictionary;
public FreezedDictionary(IReadOnlyDictionary<TKey, TValue> dictionary)
{
this.dictionary = dictionary;
}
public TValue this[TKey key] => dictionary[key];
public IEnumerable<TKey> Keys => dictionary.Keys;
public IEnumerable<TValue> Values => dictionary.Values;
public int Count => dictionary.Count;
public bool ContainsKey(TKey key)
{
return dictionary.ContainsKey(key);
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return dictionary.GetEnumerator();
}
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
{
return dictionary.TryGetValue(key, out value);
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)dictionary).GetEnumerator();
}
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, bool reverse = false)
{
return new FreezedView<KeyValuePair<TKey, TValue>, TView>(dictionary, transform, reverse);
}
public ISortableSynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateSortableView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform)
{
return new FreezedSortableView<KeyValuePair<TKey, TValue>, TView>(dictionary, transform);
}
}
}

View File

@ -2,7 +2,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
namespace ObservableCollections
@ -36,12 +35,12 @@ namespace ObservableCollections
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
{
return new View<TView>(this, transform, reverse);
return new FreezedView<T, TView>(list, transform, reverse);
}
public ISortableSynchronizedView<T, TView> CreateSortableView<TView>(Func<T, TView> transform)
{
return new SortableView<TView>(this, transform);
return new FreezedSortableView<T, TView>(list, transform);
}
public bool Contains(T item)
@ -58,200 +57,5 @@ namespace ObservableCollections
{
return GetEnumerator();
}
class View<TView> : ISynchronizedView<T, TView>
{
readonly bool reverse;
readonly List<(T, TView)> list;
ISynchronizedViewFilter<T, TView> filter;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
public object SyncRoot { get; } = new object();
public View(FreezedList<T> source, Func<T, TView> selector, bool reverse)
{
this.reverse = reverse;
this.filter = SynchronizedViewFilter<T, TView>.AlwaysTrue;
this.list = source.Select(x => (x, selector(x))).ToList();
}
public int Count
{
get
{
lock (SyncRoot)
{
return list.Count;
}
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in list)
{
filter.Invoke(value, view);
}
}
}
public void ResetFilter(Action<T, TView>? resetAction)
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T, TView>.AlwaysTrue;
if (resetAction != null)
{
foreach (var (item, view) in list)
{
resetAction(item, view);
}
}
}
}
public IEnumerator<(T, TView)> GetEnumerator()
{
if (!reverse)
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, list.GetEnumerator(), filter);
}
else
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, list.AsEnumerable().Reverse().GetEnumerator(), filter);
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Dispose()
{
}
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
{
return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
}
}
class SortableView<TView> : ISortableSynchronizedView<T, TView>
{
readonly (T, TView)[] array;
ISynchronizedViewFilter<T, TView> filter;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
public object SyncRoot { get; } = new object();
public SortableView(FreezedList<T> source, Func<T, TView> selector)
{
this.filter = SynchronizedViewFilter<T, TView>.AlwaysTrue;
this.array = source.Select(x => (x, selector(x))).ToArray();
}
public int Count
{
get
{
lock (SyncRoot)
{
return array.Length;
}
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in array)
{
filter.Invoke(value, view);
}
}
}
public void ResetFilter(Action<T, TView>? resetAction)
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T, TView>.AlwaysTrue;
if (resetAction != null)
{
foreach (var (item, view) in array)
{
resetAction(item, view);
}
}
}
}
public IEnumerator<(T, TView)> GetEnumerator()
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, array.AsEnumerable().GetEnumerator(), filter);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Dispose()
{
}
public void Sort(IComparer<T> comparer)
{
Array.Sort(array, new TComparer(comparer));
}
public void Sort(IComparer<TView> viewComparer)
{
Array.Sort(array, new TViewComparer(viewComparer));
}
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
{
return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
}
class TComparer : IComparer<(T, TView)>
{
readonly IComparer<T> comparer;
public TComparer(IComparer<T> comparer)
{
this.comparer = comparer;
}
public int Compare((T, TView) x, (T, TView) y)
{
return comparer.Compare(x.Item1, y.Item1);
}
}
class TViewComparer : IComparer<(T, TView)>
{
readonly IComparer<TView> comparer;
public TViewComparer(IComparer<TView> comparer)
{
this.comparer = comparer;
}
public int Compare((T, TView) x, (T, TView) y)
{
return comparer.Compare(x.Item2, y.Item2);
}
}
}
}
}

View File

@ -0,0 +1,203 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
namespace ObservableCollections.Internal
{
internal sealed class FreezedView<T, TView> : ISynchronizedView<T, TView>
{
readonly bool reverse;
readonly List<(T, TView)> list;
ISynchronizedViewFilter<T, TView> filter;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
public object SyncRoot { get; } = new object();
public FreezedView(IEnumerable<T> source, Func<T, TView> selector, bool reverse)
{
this.reverse = reverse;
this.filter = SynchronizedViewFilter<T, TView>.AlwaysTrue;
this.list = source.Select(x => (x, selector(x))).ToList();
}
public int Count
{
get
{
lock (SyncRoot)
{
return list.Count;
}
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in list)
{
filter.Invoke(value, view);
}
}
}
public void ResetFilter(Action<T, TView>? resetAction)
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T, TView>.AlwaysTrue;
if (resetAction != null)
{
foreach (var (item, view) in list)
{
resetAction(item, view);
}
}
}
}
public IEnumerator<(T, TView)> GetEnumerator()
{
if (!reverse)
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, list.GetEnumerator(), filter);
}
else
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, list.AsEnumerable().Reverse().GetEnumerator(), filter);
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Dispose()
{
}
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
{
return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
}
}
internal sealed class FreezedSortableView<T, TView> : ISortableSynchronizedView<T, TView>
{
readonly (T, TView)[] array;
ISynchronizedViewFilter<T, TView> filter;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
public object SyncRoot { get; } = new object();
public FreezedSortableView(IEnumerable<T> source, Func<T, TView> selector)
{
this.filter = SynchronizedViewFilter<T, TView>.AlwaysTrue;
this.array = source.Select(x => (x, selector(x))).ToArray();
}
public int Count
{
get
{
lock (SyncRoot)
{
return array.Length;
}
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in array)
{
filter.Invoke(value, view);
}
}
}
public void ResetFilter(Action<T, TView>? resetAction)
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T, TView>.AlwaysTrue;
if (resetAction != null)
{
foreach (var (item, view) in array)
{
resetAction(item, view);
}
}
}
}
public IEnumerator<(T, TView)> GetEnumerator()
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, array.AsEnumerable().GetEnumerator(), filter);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Dispose()
{
}
public void Sort(IComparer<T> comparer)
{
Array.Sort(array, new TComparer(comparer));
}
public void Sort(IComparer<TView> viewComparer)
{
Array.Sort(array, new TViewComparer(viewComparer));
}
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
{
return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
}
class TComparer : IComparer<(T, TView)>
{
readonly IComparer<T> comparer;
public TComparer(IComparer<T> comparer)
{
this.comparer = comparer;
}
public int Compare((T, TView) x, (T, TView) y)
{
return comparer.Compare(x.Item1, y.Item1);
}
}
class TViewComparer : IComparer<(T, TView)>
{
readonly IComparer<TView> comparer;
public TViewComparer(IComparer<TView> comparer)
{
this.comparer = comparer;
}
public int Compare((T, TView) x, (T, TView) y)
{
return comparer.Compare(x.Item2, y.Item2);
}
}
}
}

View File

@ -4,27 +4,25 @@ using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObservableCollections
{
public sealed partial class ObservableDictionary<TKey, TValue>
{
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, bool reverse = false)
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, bool _ = false)
{
// reverse is no used.
throw new NotImplementedException();
return new View<TView>(this, transform);
}
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateSortedView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, IComparer<KeyValuePair<TKey, TValue>> comparer)
{
throw new NotImplementedException();
return new SortedView<TView>(this, transform, comparer);
}
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateSortedView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, IComparer<TView> viewComparer)
{
throw new NotImplementedException();
return new ViewComparerSortedView<TView>(this, transform, viewComparer);
}
class View<TView> : ISynchronizedView<KeyValuePair<TKey, TValue>, TView>
@ -293,5 +291,152 @@ namespace ObservableCollections
}
}
}
#pragma warning disable CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint.
class ViewComparerSortedView<TView> : ISynchronizedView<KeyValuePair<TKey, TValue>, TView>
{
readonly ObservableDictionary<TKey, TValue> source;
readonly Func<KeyValuePair<TKey, TValue>, TView> selector;
readonly Dictionary<TKey, TView> viewMap;
readonly SortedDictionary<TView, KeyValuePair<TKey, TValue>> dict;
ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter;
public ViewComparerSortedView(ObservableDictionary<TKey, TValue> source, Func<KeyValuePair<TKey, TValue>, TView> selector, IComparer<TView> viewComparer)
{
this.source = source;
this.selector = selector;
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView>.AlwaysTrue;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
this.viewMap = new Dictionary<TKey, TView>(source.Count);
this.dict = new SortedDictionary<TView, KeyValuePair<TKey, TValue>>(viewComparer);
foreach (var item in source.dictionary)
{
var v = selector(item);
dict.Add(v, item);
viewMap.Add(item.Key, v);
}
this.source.CollectionChanged += SourceCollectionChanged;
}
}
public object SyncRoot { get; }
public event NotifyCollectionChangedEventHandler<KeyValuePair<TKey, TValue>>? RoutingCollectionChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public int Count
{
get
{
lock (SyncRoot)
{
return dict.Count;
}
}
}
public void Dispose()
{
this.source.CollectionChanged -= SourceCollectionChanged;
}
public void AttachFilter(ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var v in dict)
{
filter.Invoke(v.Value, v.Key);
}
}
}
public void ResetFilter(Action<KeyValuePair<TKey, TValue>, TView>? resetAction)
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView>.AlwaysTrue;
if (resetAction != null)
{
foreach (var v in dict)
{
resetAction(v.Value, v.Key);
}
}
}
}
public INotifyCollectionChangedSynchronizedView<KeyValuePair<TKey, TValue>, TView> WithINotifyCollectionChanged()
{
lock (SyncRoot)
{
return new NotifyCollectionChangedSynchronizedView<KeyValuePair<TKey, TValue>, TView>(this);
}
}
public IEnumerator<(KeyValuePair<TKey, TValue>, TView)> GetEnumerator()
{
return new SynchronizedViewEnumerator<KeyValuePair<TKey, TValue>, TView>(SyncRoot,
dict.Select(x => (x.Value, x.Key)).GetEnumerator(),
filter);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>> e)
{
// ObservableDictionary only provides single item operation and does not use int index.
lock (SyncRoot)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
{
var v = selector(e.NewItem);
var k = new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value);
dict.Add(v, k);
viewMap.Add(e.NewItem.Key, v);
filter.Invoke(k, v);
}
break;
case NotifyCollectionChangedAction.Remove:
{
if (viewMap.Remove(e.OldItem.Key, out var view))
{
dict.Remove(view);
}
}
break;
case NotifyCollectionChangedAction.Move:
case NotifyCollectionChangedAction.Replace:
{
if (viewMap.Remove(e.OldItem.Key, out var view))
{
dict.Remove(view);
}
goto case NotifyCollectionChangedAction.Add;
}
case NotifyCollectionChangedAction.Reset:
{
dict.Clear();
}
break;
default:
break;
}
RoutingCollectionChanged?.Invoke(e);
CollectionStateChanged?.Invoke(e.Action);
}
}
}
#pragma warning restore CS8714
}
}