Observable Dict
This commit is contained in:
parent
4946d82444
commit
327850a0db
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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
|
||||||
{
|
{
|
||||||
|
46
src/ObservableCollections/Internal/SynchronizedEnumerator.cs
Normal file
46
src/ObservableCollections/Internal/SynchronizedEnumerator.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
297
src/ObservableCollections/ObservableDictionary.Views.cs
Normal file
297
src/ObservableCollections/ObservableDictionary.Views.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
206
src/ObservableCollections/ObservableDictionary.cs
Normal file
206
src/ObservableCollections/ObservableDictionary.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -10,5 +10,6 @@ namespace ObservableCollections.Tests
|
|||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user