Observable Dict

This commit is contained in:
neuecc 2021-08-04 15:49:48 +09:00
parent 4946d82444
commit 327850a0db
10 changed files with 801 additions and 237 deletions

View File

@ -1,249 +1,257 @@
//using ObservableCollections.Internal; using ObservableCollections.Internal;
//using ObservableCollections; using System;
//using System; using System.Collections;
//using System.Collections; using System.Collections.Generic;
//using System.Collections.Generic; using System.Collections.Specialized;
//using System.Collections.Specialized; using System.Linq;
//using System.Linq;
//using System.Runtime.InteropServices;
//using System.Text;
//using System.Threading.Tasks;
//namespace ObservableCollections namespace ObservableCollections
//{ {
// public sealed class FreezedList<T> : IReadOnlyList<T>, IFreezedCollection<T> public sealed class FreezedList<T> : IReadOnlyList<T>, IFreezedCollection<T>
// { {
// readonly IReadOnlyList<T> list; readonly IReadOnlyList<T> list;
// public T this[int index] public T this[int index]
// { {
// get get
// { {
// return list[index]; return list[index];
// } }
// } }
// public int Count public int Count
// { {
// get get
// { {
// return list.Count; return list.Count;
// } }
// } }
// public bool IsReadOnly => true; public bool IsReadOnly => true;
// public FreezedList(IReadOnlyList<T> list) public FreezedList(IReadOnlyList<T> list)
// { {
// this.list = list; this.list = list;
// } }
// public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false) public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
// { {
// return new View<TView>(this, transform, reverse); return new View<TView>(this, transform, reverse);
// } }
// public ISortableSynchronizedView<T, TView> CreateSortableView<TView>(Func<T, TView> transform) public ISortableSynchronizedView<T, TView> CreateSortableView<TView>(Func<T, TView> transform)
// { {
// return new SortableView<TView>(this, transform); return new SortableView<TView>(this, transform);
// } }
// public bool Contains(T item) public bool Contains(T item)
// { {
// return list.Contains(item); return list.Contains(item);
// } }
// public IEnumerator<T> GetEnumerator() public IEnumerator<T> GetEnumerator()
// { {
// return list.GetEnumerator(); return list.GetEnumerator();
// } }
// IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
// { {
// return GetEnumerator(); return GetEnumerator();
// } }
// class View<TView> : ISynchronizedView<T, TView> class View<TView> : ISynchronizedView<T, TView>
// { {
// readonly bool reverse; readonly bool reverse;
// readonly List<(T, TView)> list; readonly List<(T, TView)> list;
// ISynchronizedViewFilter<T, TView> filter; ISynchronizedViewFilter<T, TView> filter;
// public event Action<NotifyCollectionChangedAction>? CollectionStateChanged; public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
// public object SyncRoot { get; } = new object(); public object SyncRoot { get; } = new object();
// public View(FreezedList<T> source, Func<T, TView> selector, bool reverse) public View(FreezedList<T> source, Func<T, TView> selector, bool reverse)
// { {
// this.reverse = reverse; this.reverse = reverse;
// this.filter = TrueViewFilter<T, TView>.Instance; this.filter = SynchronizedViewFilter<T, TView>.AlwaysTrue;
// this.list = source.Select(x => (x, selector(x))).ToList(); this.list = source.Select(x => (x, selector(x))).ToList();
// } }
// public int Count public int Count
// { {
// get get
// { {
// lock (SyncRoot) lock (SyncRoot)
// { {
// return list.Count; return list.Count;
// } }
// } }
// } }
// public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
// { {
// lock (SyncRoot) lock (SyncRoot)
// { {
// this.filter = filter; this.filter = filter;
// foreach (var (value, view) in list) foreach (var (value, view) in list)
// { {
// filter.Invoke(value, view); filter.Invoke(value, view);
// } }
// } }
// } }
// public void ResetFilter(Action<T, TView>? resetAction) public void ResetFilter(Action<T, TView>? resetAction)
// { {
// lock (SyncRoot) lock (SyncRoot)
// { {
// this.filter = TrueViewFilter<T, TView>.Instance; this.filter = SynchronizedViewFilter<T, TView>.AlwaysTrue;
// if (resetAction != null) if (resetAction != null)
// { {
// foreach (var (item, view) in list) foreach (var (item, view) in list)
// { {
// resetAction(item, view); resetAction(item, view);
// } }
// } }
// } }
// } }
// public IEnumerator<(T, TView)> GetEnumerator() public IEnumerator<(T, TView)> GetEnumerator()
// { {
// if (!reverse) if (!reverse)
// { {
// return new SynchronizedViewEnumerator<T, TView>(SyncRoot, list.GetEnumerator(), filter); return new SynchronizedViewEnumerator<T, TView>(SyncRoot, list.GetEnumerator(), filter);
// } }
// else else
// { {
// return new SynchronizedViewEnumerator<T, TView>(SyncRoot, list.AsEnumerable().Reverse().GetEnumerator(), filter); return new SynchronizedViewEnumerator<T, TView>(SyncRoot, list.AsEnumerable().Reverse().GetEnumerator(), filter);
// } }
// } }
// IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
// public void Dispose() public void Dispose()
// { {
// } }
// }
// class SortableView<TView> : ISortableSynchronizedView<T, TView> public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
// { {
// readonly (T, TView)[] array; return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
}
}
// ISynchronizedViewFilter<T, TView> filter; class SortableView<TView> : ISortableSynchronizedView<T, TView>
{
readonly (T, TView)[] array;
// public event Action<NotifyCollectionChangedAction>? CollectionStateChanged; ISynchronizedViewFilter<T, TView> filter;
// public object SyncRoot { get; } = new object(); public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
// public SortableView(FreezedList<T> source, Func<T, TView> selector) public object SyncRoot { get; } = new object();
// {
// this.filter = TrueViewFilter<T, TView>.Instance;
// this.array = source.Select(x => (x, selector(x))).ToArray();
// }
// public int Count public SortableView(FreezedList<T> source, Func<T, TView> selector)
// { {
// get this.filter = SynchronizedViewFilter<T, TView>.AlwaysTrue;
// { this.array = source.Select(x => (x, selector(x))).ToArray();
// lock (SyncRoot) }
// {
// return array.Length;
// }
// }
// }
// public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public int Count
// { {
// lock (SyncRoot) get
// { {
// this.filter = filter; lock (SyncRoot)
// foreach (var (value, view) in array) {
// { return array.Length;
// filter.Invoke(value, view); }
// } }
// } }
// }
// public void ResetFilter(Action<T, TView>? resetAction) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
// { {
// lock (SyncRoot) lock (SyncRoot)
// { {
// this.filter = TrueViewFilter<T, TView>.Instance; this.filter = filter;
// if (resetAction != null) foreach (var (value, view) in array)
// { {
// foreach (var (item, view) in array) filter.Invoke(value, view);
// { }
// resetAction(item, view); }
// } }
// }
// }
// }
// public IEnumerator<(T, TView)> GetEnumerator() public void ResetFilter(Action<T, TView>? resetAction)
// { {
// return new SynchronizedViewEnumerator<T, TView>(SyncRoot, array.AsEnumerable().GetEnumerator(), filter); lock (SyncRoot)
// } {
this.filter = SynchronizedViewFilter<T, TView>.AlwaysTrue;
if (resetAction != null)
{
foreach (var (item, view) in array)
{
resetAction(item, view);
}
}
}
}
// IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public IEnumerator<(T, TView)> GetEnumerator()
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, array.AsEnumerable().GetEnumerator(), filter);
}
// public void Dispose() 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) public void Sort(IComparer<T> comparer)
// { {
// Array.Sort(array, new TViewComparer(viewComparer)); Array.Sort(array, new TComparer(comparer));
// } }
// class TComparer : IComparer<(T, TView)> public void Sort(IComparer<TView> viewComparer)
// { {
// readonly IComparer<T> comparer; Array.Sort(array, new TViewComparer(viewComparer));
}
// public TComparer(IComparer<T> comparer) public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
// { {
// this.comparer = comparer; return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
// } }
// public int Compare((T, TView) x, (T, TView) y) class TComparer : IComparer<(T, TView)>
// { {
// return comparer.Compare(x.Item1, y.Item1); readonly IComparer<T> comparer;
// }
// }
// class TViewComparer : IComparer<(T, TView)> public TComparer(IComparer<T> comparer)
// { {
// readonly IComparer<TView> comparer; this.comparer = comparer;
}
// public TViewComparer(IComparer<TView> comparer) public int Compare((T, TView) x, (T, TView) y)
// { {
// this.comparer = comparer; return comparer.Compare(x.Item1, y.Item1);
// } }
}
// public int Compare((T, TView) x, (T, TView) y) class TViewComparer : IComparer<(T, TView)>
// { {
// return comparer.Compare(x.Item2, y.Item2); 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

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.ComponentModel;
namespace ObservableCollections namespace ObservableCollections
{ {
@ -13,9 +14,6 @@ namespace ObservableCollections
ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false); ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false);
ISynchronizedView<T, TView> CreateSortedView<TView>(Func<T, TView> transform, IComparer<T> comparer); ISynchronizedView<T, TView> CreateSortedView<TView>(Func<T, TView> transform, IComparer<T> comparer);
ISynchronizedView<T, TView> CreateSortedView<TView>(Func<T, TView> transform, IComparer<TView> viewComparer); ISynchronizedView<T, TView> CreateSortedView<TView>(Func<T, TView> transform, IComparer<TView> viewComparer);
// TODO:Grouping
// IGroupedSynchronizedView<T, TKey, TView> CreateGroupedView<TKey, TView>(Func<T, TKey> keySelector, Func<T, TView> transform);
} }
public interface IFreezedCollection<T> public interface IFreezedCollection<T>
@ -42,7 +40,7 @@ namespace ObservableCollections
void Sort(IComparer<TView> viewComparer); void Sort(IComparer<TView> viewComparer);
} }
public interface INotifyCollectionChangedSynchronizedView<T, TView> : ISynchronizedView<T, TView>, INotifyCollectionChanged public interface INotifyCollectionChangedSynchronizedView<T, TView> : ISynchronizedView<T, TView>, INotifyCollectionChanged, INotifyPropertyChanged
{ {
} }
} }

View File

@ -2,12 +2,14 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.ComponentModel;
namespace ObservableCollections.Internal namespace ObservableCollections.Internal
{ {
internal class NotifyCollectionChangedSynchronizedView<T, TView> : INotifyCollectionChangedSynchronizedView<T, TView> internal class NotifyCollectionChangedSynchronizedView<T, TView> : INotifyCollectionChangedSynchronizedView<T, TView>
{ {
readonly ISynchronizedView<T, TView> parent; readonly ISynchronizedView<T, TView> parent;
static readonly PropertyChangedEventArgs CountPropertyChangedEventArgs = new PropertyChangedEventArgs("Count");
public NotifyCollectionChangedSynchronizedView(ISynchronizedView<T, TView> parent) public NotifyCollectionChangedSynchronizedView(ISynchronizedView<T, TView> parent)
{ {
@ -18,6 +20,20 @@ namespace ObservableCollections.Internal
private void Parent_RoutingCollectionChanged(in NotifyCollectionChangedEventArgs<T> e) private void Parent_RoutingCollectionChanged(in NotifyCollectionChangedEventArgs<T> e)
{ {
CollectionChanged?.Invoke(this, e.ToStandardEventArgs()); CollectionChanged?.Invoke(this, e.ToStandardEventArgs());
switch (e.Action)
{
// add, remove, reset will change the count.
case NotifyCollectionChangedAction.Add:
case NotifyCollectionChangedAction.Remove:
case NotifyCollectionChangedAction.Reset:
PropertyChanged?.Invoke(this, CountPropertyChangedEventArgs);
break;
case NotifyCollectionChangedAction.Replace:
case NotifyCollectionChangedAction.Move:
default:
break;
}
} }
public object SyncRoot => parent.SyncRoot; public object SyncRoot => parent.SyncRoot;
@ -25,6 +41,7 @@ namespace ObservableCollections.Internal
public int Count => parent.Count; public int Count => parent.Count;
public event NotifyCollectionChangedEventHandler? CollectionChanged; public event NotifyCollectionChangedEventHandler? CollectionChanged;
public event PropertyChangedEventHandler? PropertyChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged public event Action<NotifyCollectionChangedAction>? CollectionStateChanged
{ {

View File

@ -0,0 +1,46 @@
using System.Collections;
using System.Collections.Generic;
using System.Threading;
namespace ObservableCollections.Internal
{
internal class SynchronizedEnumerator<T> : IEnumerator<T>
{
bool isDisposed;
readonly object gate;
readonly bool lockTaken;
readonly IEnumerator<T> enumerator;
public SynchronizedEnumerator(object gate, IEnumerator<T> enumerator)
{
this.gate = gate;
this.enumerator = enumerator;
Monitor.Enter(gate, ref lockTaken);
}
public T Current => enumerator.Current;
object IEnumerator.Current => Current!;
public bool MoveNext() => enumerator.MoveNext();
public void Reset() => enumerator.Reset();
public void Dispose()
{
if (!isDisposed)
{
isDisposed = true;
try
{
enumerator.Dispose();
}
finally
{
if (lockTaken)
{
Monitor.Exit(gate);
}
}
}
}
}
}

View File

@ -8,8 +8,7 @@ namespace ObservableCollections.Internal
internal class SynchronizedViewEnumerator<T, TView> : IEnumerator<(T, TView)>, IDisposable internal class SynchronizedViewEnumerator<T, TView> : IEnumerator<(T, TView)>, IDisposable
{ {
bool isDisposed; bool isDisposed;
bool startEnumerate; readonly bool lockTaken;
bool lockTaken;
readonly object gate; readonly object gate;
readonly IEnumerator<(T, TView)> enumerator; readonly IEnumerator<(T, TView)> enumerator;
readonly ISynchronizedViewFilter<T, TView> filter; readonly ISynchronizedViewFilter<T, TView> filter;
@ -22,8 +21,7 @@ namespace ObservableCollections.Internal
this.filter = filter; this.filter = filter;
this.current = default; this.current = default;
this.isDisposed = false; this.isDisposed = false;
this.startEnumerate = false; Monitor.Enter(gate, ref lockTaken);
this.lockTaken = false;
} }
public (T, TView) Current => current; public (T, TView) Current => current;
@ -31,12 +29,6 @@ namespace ObservableCollections.Internal
public bool MoveNext() public bool MoveNext()
{ {
if (!startEnumerate) // TODO: check is this work correctly?
{
startEnumerate = true;
Monitor.Enter(gate, ref lockTaken);
}
while (enumerator.MoveNext()) while (enumerator.MoveNext())
{ {
current = enumerator.Current; current = enumerator.Current;
@ -51,7 +43,7 @@ namespace ObservableCollections.Internal
public void Dispose() public void Dispose()
{ {
if (isDisposed) if (!isDisposed)
{ {
isDisposed = true; isDisposed = true;
try try

View File

@ -0,0 +1,297 @@
using ObservableCollections.Internal;
using System;
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)
{
// reverse is no used.
throw new NotImplementedException();
}
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateSortedView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, IComparer<KeyValuePair<TKey, TValue>> comparer)
{
throw new NotImplementedException();
}
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateSortedView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, IComparer<TView> viewComparer)
{
throw new NotImplementedException();
}
class View<TView> : ISynchronizedView<KeyValuePair<TKey, TValue>, TView>
{
readonly ObservableDictionary<TKey, TValue> source;
readonly Func<KeyValuePair<TKey, TValue>, TView> selector;
ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter;
readonly Dictionary<TKey, (TValue, TView)> dict;
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>.AlwaysTrue;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
this.dict = source.dictionary.ToDictionary(x => x.Key, x => (x.Value, selector(x)));
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(new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1), v.Value.Item2);
}
}
}
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(new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1), v.Value.Item2);
}
}
}
}
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 => (new KeyValuePair<TKey, TValue>(x.Key, x.Value.Item1), x.Value.Item2)).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);
dict.Add(e.NewItem.Key, (e.NewItem.Value, v));
filter.Invoke(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v);
}
break;
case NotifyCollectionChangedAction.Remove:
{
dict.Remove(e.OldItem.Key);
}
break;
case NotifyCollectionChangedAction.Move:
case NotifyCollectionChangedAction.Replace:
{
dict.Remove(e.OldItem.Key);
var v = selector(e.NewItem);
dict.Add(e.NewItem.Key, (e.NewItem.Value, v));
filter.Invoke(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v);
}
break;
case NotifyCollectionChangedAction.Reset:
{
dict.Clear();
}
break;
default:
break;
}
RoutingCollectionChanged?.Invoke(e);
CollectionStateChanged?.Invoke(e.Action);
}
}
}
class SortedView<TView> : ISynchronizedView<KeyValuePair<TKey, TValue>, TView>
{
readonly ObservableDictionary<TKey, TValue> source;
readonly Func<KeyValuePair<TKey, TValue>, TView> selector;
ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter;
readonly SortedDictionary<KeyValuePair<TKey, TValue>, TView> dict;
public SortedView(ObservableDictionary<TKey, TValue> source, Func<KeyValuePair<TKey, TValue>, TView> selector, IComparer<KeyValuePair<TKey, TValue>> comparer)
{
this.source = source;
this.selector = selector;
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView>.AlwaysTrue;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
this.dict = new SortedDictionary<KeyValuePair<TKey, TValue>, TView>(comparer);
foreach (var item in source.dictionary)
{
dict.Add(item, selector(item));
}
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(new KeyValuePair<TKey, TValue>(v.Key.Key, v.Key.Value), v.Value);
}
}
}
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(new KeyValuePair<TKey, TValue>(v.Key.Key, v.Key.Value), v.Value);
}
}
}
}
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 => (new KeyValuePair<TKey, TValue>(x.Key.Key, x.Key.Value), x.Value)).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(k, v);
filter.Invoke(k, v);
}
break;
case NotifyCollectionChangedAction.Remove:
{
dict.Remove(e.OldItem);
}
break;
case NotifyCollectionChangedAction.Move:
case NotifyCollectionChangedAction.Replace:
{
var k = new KeyValuePair<TKey, TValue>(e.OldItem.Key, e.OldItem.Value);
dict.Remove(k);
var v = selector(e.NewItem);
var nk = new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value);
dict.Add(nk, v);
filter.Invoke(nk, v);
}
break;
case NotifyCollectionChangedAction.Reset:
{
dict.Clear();
}
break;
default:
break;
}
RoutingCollectionChanged?.Invoke(e);
CollectionStateChanged?.Invoke(e.Action);
}
}
}
}
}

View File

@ -0,0 +1,206 @@
using ObservableCollections.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace ObservableCollections
{
public sealed partial class ObservableDictionary<TKey, TValue>
: IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue>, IObservableCollection<KeyValuePair<TKey, TValue>>
where TKey : notnull
{
readonly Dictionary<TKey, TValue> dictionary;
public readonly object SyncRoot = new object();
public ObservableDictionary()
{
this.dictionary = new Dictionary<TKey, TValue>();
}
public ObservableDictionary(Dictionary<TKey, TValue> dictionary)
{
this.dictionary = dictionary;
}
public event NotifyCollectionChangedEventHandler<KeyValuePair<TKey, TValue>>? CollectionChanged;
public TValue this[TKey key]
{
get
{
lock (SyncRoot)
{
return dictionary[key];
}
}
set
{
lock (SyncRoot)
{
var oldValue = dictionary[key];
dictionary[key] = value;
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Replace(
new KeyValuePair<TKey, TValue>(key, value),
new KeyValuePair<TKey, TValue>(key, oldValue),
-1));
}
}
}
// for lock synchronization, hide keys and values.
ICollection<TKey> IDictionary<TKey, TValue>.Keys
{
get
{
lock (SyncRoot)
{
return dictionary.Keys;
}
}
}
ICollection<TValue> IDictionary<TKey, TValue>.Values
{
get
{
lock (SyncRoot)
{
return dictionary.Values;
}
}
}
public int Count
{
get
{
lock (SyncRoot)
{
return dictionary.Count;
}
}
}
public bool IsReadOnly => false;
IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys
{
get
{
lock (SyncRoot)
{
return dictionary.Keys;
}
}
}
IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values
{
get
{
lock (SyncRoot)
{
return dictionary.Values;
}
}
}
public void Add(TKey key, TValue value)
{
lock (SyncRoot)
{
dictionary.Add(key, value);
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Add(new KeyValuePair<TKey, TValue>(key, value), -1));
}
}
public void Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
public void Clear()
{
lock (SyncRoot)
{
dictionary.Clear();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Reset());
}
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
lock (SyncRoot)
{
return ((ICollection<KeyValuePair<TKey, TValue>>)dictionary).Contains(item);
}
}
public bool ContainsKey(TKey key)
{
lock (SyncRoot)
{
return ((IDictionary<TKey, TValue>)dictionary).ContainsKey(key);
}
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
lock (SyncRoot)
{
((ICollection<KeyValuePair<TKey, TValue>>)dictionary).CopyTo(array, arrayIndex);
}
}
public bool Remove(TKey key)
{
lock (SyncRoot)
{
if (dictionary.Remove(key, out var value))
{
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Remove(new KeyValuePair<TKey, TValue>(key, value), -1));
return true;
}
return false;
}
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
lock (SyncRoot)
{
if (dictionary.TryGetValue(item.Key, out var value))
{
if (EqualityComparer<TValue>.Default.Equals(value, item.Value))
{
if (dictionary.Remove(item.Key, out var value2))
{
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Remove(new KeyValuePair<TKey, TValue>(item.Key, value2), -1));
return true;
}
}
}
return false;
}
}
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
{
lock (SyncRoot)
{
return dictionary.TryGetValue(key, out value);
}
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return new SynchronizedEnumerator<KeyValuePair<TKey, TValue>>(SyncRoot, dictionary.GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@ -36,7 +36,7 @@ namespace ObservableCollections
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged; public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged; public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public object SyncRoot { get; } = new object(); public object SyncRoot { get; }
public View(ObservableList<T> source, Func<T, TView> selector, bool reverse) public View(ObservableList<T> source, Func<T, TView> selector, bool reverse)
{ {
@ -47,7 +47,7 @@ namespace ObservableCollections
this.SyncRoot = new object(); this.SyncRoot = new object();
lock (source.SyncRoot) lock (source.SyncRoot)
{ {
this.list = source.Select(x => (x, selector(x))).ToList(); this.list = source.list.Select(x => (x, selector(x))).ToList();
this.source.CollectionChanged += SourceCollectionChanged; this.source.CollectionChanged += SourceCollectionChanged;
} }
} }

View File

@ -2,7 +2,6 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -13,6 +12,16 @@ namespace ObservableCollections
readonly List<T> list; readonly List<T> list;
public readonly object SyncRoot = new object(); public readonly object SyncRoot = new object();
public ObservableList()
{
list = new List<T>();
}
public ObservableList(IEnumerable<T> source)
{
list = source.ToList();
}
public T this[int index] public T this[int index]
{ {
get get
@ -49,16 +58,6 @@ namespace ObservableCollections
public event NotifyCollectionChangedEventHandler<T>? CollectionChanged; public event NotifyCollectionChangedEventHandler<T>? CollectionChanged;
public ObservableList()
{
list = new List<T>();
}
public ObservableList(IEnumerable<T> source)
{
list = source.ToList();
}
public void Add(T item) public void Add(T item)
{ {
lock (SyncRoot) lock (SyncRoot)
@ -136,7 +135,7 @@ namespace ObservableCollections
public IEnumerator<T> GetEnumerator() public IEnumerator<T> GetEnumerator()
{ {
return list.GetEnumerator(); return new SynchronizedEnumerator<T>(SyncRoot, list.GetEnumerator());
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()

View File

@ -10,5 +10,6 @@ namespace ObservableCollections.Tests
{ {
} }
} }
} }