Last active
December 22, 2015 09:02
-
-
Save mntone/ccb619bf66297569c8b2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // The MIT License (MIT) | |
| // Copyright (c) 2015- mntone | |
| using System; | |
| using System.Collections; | |
| using System.Collections.Concurrent; | |
| using System.Collections.Generic; | |
| using System.Collections.Specialized; | |
| using System.ComponentModel; | |
| using System.Linq; | |
| using System.Threading; | |
| namespace Mntone.IkaConnect.Core2.Infrastructures | |
| { | |
| public interface IListEx : IList | |
| { | |
| void Move(object item, int newIndex); | |
| void MoveAt(int oldIndex, int newIndex); | |
| void Replace(object oldItem, object newItem); | |
| void ReplaceAt(int index, object newItem); | |
| } | |
| public interface IMultipleOperationList : IListEx | |
| { | |
| void AddRange(IEnumerable items); | |
| void InsertRange(int startingIndex, IEnumerable items); | |
| void RemoveRangeAt(int startingIndex, int length); | |
| void MoveRangeAt(int oldStartingIndex, int length, int newStartingIndex); | |
| void ReplaceRangeAt(int oldStartingIndex, int length, IEnumerable items); | |
| void ReplaceAll(IEnumerable items); | |
| } | |
| public interface IListEx<T> : IList<T> | |
| { | |
| void Move(T item, int newIndex); | |
| void MoveAt(int oldIndex, int newIndex); | |
| void Replace(T oldItem, T newItem); | |
| void ReplaceAt(int index, T newItem); | |
| } | |
| public interface IMultipleOperationList<T> : IListEx<T> | |
| { | |
| void AddRange(IEnumerable<T> items); | |
| void InsertRange(int startingIndex, IEnumerable<T> items); | |
| void RemoveRangeAt(int startingIndex, int length); | |
| void MoveRangeAt(int oldStartingIndex, int length, int newStartingIndex); | |
| void ReplaceRangeAt(int oldStartingIndex, int length, IEnumerable<T> items); | |
| void ReplaceAll(IEnumerable<T> items); | |
| } | |
| public sealed class ObservableBlockingCollection<T> | |
| : INotifyPropertyChanged, IMultipleOperationList, IReadOnlyList<T>, IMultipleOperationList<T>, INotifyCollectionChanged, IProducerConsumerCollection<T>, IDisposable | |
| { | |
| private static readonly PropertyChangedEventArgs ItemPropertyChangedEventArgsStatic = new PropertyChangedEventArgs("Item[]"); | |
| private static readonly PropertyChangedEventArgs CountPropertyChangedEventArgsStatic = new PropertyChangedEventArgs("Count"); | |
| private static readonly NotifyCollectionChangedEventArgs NotifyCollectionChangedEventArgsStatic = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); | |
| private readonly IList<T> _list; | |
| private readonly ReaderWriterLockSlim _lock; | |
| private bool _disposed = false; | |
| public ObservableBlockingCollection() | |
| : this(Enumerable.Empty<T>()) | |
| { } | |
| public ObservableBlockingCollection(IEnumerable<T> source) | |
| { | |
| if (source == null) throw new ArgumentNullException(nameof(source)); | |
| this._list = new List<T>(source); | |
| this._lock = new ReaderWriterLockSlim(); | |
| } | |
| public void Dispose() => this.Dispose(true); | |
| private void Dispose(bool disposing) | |
| { | |
| if (this._disposed) return; | |
| if (disposing) this._lock.Dispose(); | |
| this._disposed = true; | |
| } | |
| public int Count => this.ReadLockAction(() => this._list.Count); | |
| public bool IsReadOnly => this.ReadLockAction(() => this._list.IsReadOnly); | |
| public T this[int index] | |
| { | |
| get { return this.ReadLockAction(() => this._list[index]); } | |
| set { this.ReplaceAt(index, value); } | |
| } | |
| public bool Contains(T item) => this.ReadLockAction(() => this._list.Contains(item)); | |
| public void CopyTo(T[] array, int arrayIndex) => this.ReadLockAction(() => this._list.CopyTo(array, arrayIndex)); | |
| public int IndexOf(T item) => this.ReadLockAction(() => this._list.IndexOf(item)); | |
| public T[] ToArray() => this.ReadLockAction(() => this._list.ToArray()); | |
| public void Clear() | |
| { | |
| this.ReadWriteLockAction( | |
| () => this._list.Count, | |
| count => | |
| { | |
| if (count != 0) this._list.Clear(); | |
| }, | |
| count => | |
| { | |
| if (count != 0) | |
| { | |
| var handler = this.PropertyChanged; | |
| if (handler != null) | |
| { | |
| handler(this, ItemPropertyChangedEventArgsStatic); | |
| handler(this, CountPropertyChangedEventArgsStatic); | |
| } | |
| this.CollectionChanged?.Invoke(this, NotifyCollectionChangedEventArgsStatic); | |
| } | |
| }); | |
| } | |
| public void Add(T item) | |
| { | |
| this.ReadWriteLockAction( | |
| () => this._list.Add(item), | |
| () => this.RaisePropertyChange( | |
| args: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, this._list.Count - 1))); | |
| } | |
| public void AddRange(IEnumerable<T> items) | |
| { | |
| if (items == null) throw new ArgumentNullException(nameof(items)); | |
| var buffer = items.ToArray(); | |
| if (buffer.Length == 0) throw new ArgumentException(nameof(items)); | |
| this.ReadWriteLockAction( | |
| () => | |
| { | |
| foreach (T current in buffer) | |
| { | |
| this._list.Add(current); | |
| } | |
| }, | |
| () => this.RaisePropertyChange( | |
| args: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, buffer, this._list.Count - buffer.Length))); | |
| } | |
| public bool TryAdd(T item) | |
| { | |
| return this.ReadWriteLockAction( | |
| () => !this._list.Contains(item), | |
| result => | |
| { | |
| if (result) | |
| { | |
| this._list.Add(item); | |
| } | |
| }, | |
| result => | |
| { | |
| if (result) | |
| { | |
| this.RaisePropertyChange( | |
| args: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, this._list.Count - 1)); | |
| } | |
| }); | |
| } | |
| public void Insert(int index, T item) | |
| { | |
| if (index < 0) throw new IndexOutOfRangeException(nameof(index)); | |
| this.ReadWriteLockAction( | |
| () => this._list.Insert(index, item), | |
| () => this.RaisePropertyChange( | |
| args: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index))); | |
| } | |
| public void InsertRange(int startingIndex, IEnumerable<T> items) | |
| { | |
| if (startingIndex < 0) throw new IndexOutOfRangeException(nameof(startingIndex)); | |
| if (items == null) throw new ArgumentNullException(nameof(items)); | |
| var buffer = items.ToArray(); | |
| if (buffer.Length == 0) throw new ArgumentException(nameof(items)); | |
| this.ReadWriteLockAction( | |
| () => | |
| { | |
| int index2 = startingIndex; | |
| foreach (T current in buffer) | |
| { | |
| this._list.Insert(index2++, current); | |
| } | |
| }, | |
| () => this.RaisePropertyChange( | |
| args: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, buffer, startingIndex))); | |
| } | |
| public bool Remove(T item) | |
| { | |
| if (item == null) throw new ArgumentNullException(nameof(item)); | |
| bool result = false; | |
| this.ReadWriteLockAction( | |
| () => this._list.IndexOf(item), | |
| index => result = this._list.Remove(item), | |
| index => | |
| { | |
| if (result) | |
| { | |
| this.RaisePropertyChange( | |
| args: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index)); | |
| } | |
| }); | |
| return result; | |
| } | |
| public void RemoveAt(int index) | |
| { | |
| if (index < 0) throw new IndexOutOfRangeException(nameof(index)); | |
| this.ReadWriteLockAction( | |
| () => this._list[index], | |
| removeItem => this._list.RemoveAt(index), | |
| removeItem => this.RaisePropertyChange( | |
| args: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removeItem, index))); | |
| } | |
| public void RemoveRangeAt(int oldStartingIndex, int length) | |
| { | |
| if (oldStartingIndex < 0) throw new IndexOutOfRangeException(nameof(oldStartingIndex)); | |
| if (length <= 0) throw new ArgumentOutOfRangeException(nameof(length)); | |
| this.ReadWriteLockAction( | |
| () => | |
| { | |
| var items = new T[length]; | |
| this._list.CopyTo(items, length); | |
| return items; | |
| }, | |
| _ => | |
| { | |
| var count = length; | |
| while (count-- > 0) | |
| { | |
| this._list.RemoveAt(oldStartingIndex); | |
| } | |
| }, | |
| items => | |
| { | |
| this.RaisePropertyChange( | |
| args: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, items, oldStartingIndex)); | |
| }); | |
| } | |
| public void Move(T item, int newIndex) | |
| { | |
| if (item == null) throw new ArgumentNullException(nameof(item)); | |
| if (newIndex < 0) throw new IndexOutOfRangeException(nameof(newIndex)); | |
| this.ReadWriteLockAction( | |
| () => this._list.IndexOf(item), | |
| oldIndex => | |
| { | |
| this._list.RemoveAt(oldIndex); | |
| this._list.Insert(newIndex, item); | |
| }, | |
| oldIndex => | |
| { | |
| this.RaisePropertyChange( | |
| args: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, item, newIndex, oldIndex), | |
| isCount: false); | |
| }); | |
| } | |
| public void MoveAt(int oldIndex, int newIndex) | |
| { | |
| if (oldIndex < 0) throw new IndexOutOfRangeException(nameof(oldIndex)); | |
| if (newIndex < 0) throw new IndexOutOfRangeException(nameof(newIndex)); | |
| this.ReadWriteLockAction( | |
| () => this._list[oldIndex], | |
| item => | |
| { | |
| this._list.RemoveAt(oldIndex); | |
| this._list.Insert(newIndex, item); | |
| }, | |
| item => | |
| { | |
| this.RaisePropertyChange( | |
| args: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, item, newIndex, oldIndex), | |
| isCount: false); | |
| }); | |
| } | |
| public void MoveRangeAt(int oldStartingIndex, int length, int newStartingIndex) | |
| { | |
| if (oldStartingIndex < 0) throw new IndexOutOfRangeException(nameof(oldStartingIndex)); | |
| if (length <= 0) throw new ArgumentOutOfRangeException(nameof(length)); | |
| if (newStartingIndex < 0) throw new IndexOutOfRangeException(nameof(newStartingIndex)); | |
| this.ReadWriteLockAction( | |
| () => | |
| { | |
| var items = new T[length]; | |
| this._list.CopyTo(items, length); | |
| return items; | |
| }, | |
| items => | |
| { | |
| var count = length; | |
| while (count-- > 0) | |
| { | |
| this._list.RemoveAt(oldStartingIndex); | |
| } | |
| foreach (var item2 in items) | |
| { | |
| this._list.Insert(newStartingIndex, item2); | |
| } | |
| }, | |
| items => | |
| { | |
| this.RaisePropertyChange( | |
| args: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, items, newStartingIndex, oldStartingIndex), | |
| isCount: false); | |
| }); | |
| } | |
| public void Replace(T oldItem, T newItem) | |
| { | |
| if (oldItem == null) throw new ArgumentNullException(nameof(oldItem)); | |
| if (newItem == null) throw new ArgumentNullException(nameof(newItem)); | |
| this.ReadWriteLockAction( | |
| () => this._list.IndexOf(oldItem), | |
| index => this._list[index] = newItem, | |
| index => | |
| { | |
| this.RaisePropertyChange( | |
| args: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItem, oldItem, index), | |
| isCount: false); | |
| }); | |
| } | |
| public void ReplaceAt(int index, T newItem) | |
| { | |
| if (index < 0) throw new IndexOutOfRangeException(nameof(index)); | |
| if (newItem == null) throw new ArgumentNullException(nameof(newItem)); | |
| this.ReadWriteLockAction( | |
| () => this._list[index], | |
| oldItem => this._list[index] = newItem, | |
| oldItem => | |
| { | |
| this.RaisePropertyChange( | |
| args: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItem, oldItem, index), | |
| isCount: false); | |
| }); | |
| } | |
| public void ReplaceRangeAt(int oldStartingIndex, int length, IEnumerable<T> items) | |
| { | |
| if (oldStartingIndex < 0) throw new IndexOutOfRangeException(nameof(oldStartingIndex)); | |
| if (length <= 0) throw new ArgumentOutOfRangeException(nameof(length)); | |
| if (items == null) throw new ArgumentNullException(nameof(items)); | |
| var buffer = items.ToArray(); | |
| if (buffer.Length == 0) throw new ArgumentException(nameof(items)); | |
| this.ReadWriteLockAction( | |
| () => | |
| { | |
| var oldItems = new T[length]; | |
| this._list.CopyTo(oldItems, oldStartingIndex); | |
| return oldItems; | |
| }, | |
| _ => | |
| { | |
| var index2 = oldStartingIndex; | |
| foreach (T current in buffer) | |
| { | |
| this._list.Insert(index2++, current); | |
| } | |
| }, | |
| oldItems => this.RaisePropertyChange( | |
| args: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, buffer, oldItems, oldStartingIndex))); | |
| } | |
| public void ReplaceAll(IEnumerable<T> items) | |
| { | |
| if (items == null) throw new ArgumentNullException(nameof(items)); | |
| var buffer = items.ToArray(); | |
| if (buffer.Length == 0) throw new ArgumentException(nameof(items)); | |
| this.ReadWriteLockAction( | |
| () => | |
| { | |
| var oldItems = new T[this._list.Count]; | |
| this._list.CopyTo(oldItems, 0); | |
| return oldItems; | |
| }, | |
| _ => | |
| { | |
| this._list.Clear(); | |
| foreach (T current in buffer) | |
| { | |
| this._list.Add(current); | |
| } | |
| }, | |
| oldItems => this.RaisePropertyChange( | |
| args: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, buffer, oldItems, 0))); | |
| } | |
| public bool TryTake(out T item) | |
| { | |
| throw new NotImplementedException(); | |
| } | |
| private void RaisePropertyChange(NotifyCollectionChangedEventArgs args, bool isCount = true) | |
| { | |
| var handler = this.PropertyChanged; | |
| if (handler != null) | |
| { | |
| handler(this, ItemPropertyChangedEventArgsStatic); | |
| if (isCount) handler(this, CountPropertyChangedEventArgsStatic); | |
| } | |
| this.CollectionChanged?.Invoke(this, args); | |
| } | |
| private void ReadLockAction(Action readAction) | |
| { | |
| if (!this._lock.IsReadLockHeld) | |
| { | |
| this._lock.EnterReadLock(); | |
| try | |
| { | |
| readAction(); | |
| return; | |
| } | |
| finally | |
| { | |
| this._lock.ExitReadLock(); | |
| } | |
| } | |
| readAction(); | |
| } | |
| private TResult ReadLockAction<TResult>(Func<TResult> readAction) | |
| { | |
| if (!this._lock.IsReadLockHeld) | |
| { | |
| this._lock.EnterReadLock(); | |
| try | |
| { | |
| return readAction(); | |
| } | |
| finally | |
| { | |
| this._lock.ExitReadLock(); | |
| } | |
| } | |
| return readAction(); | |
| } | |
| private void ReadWriteLockAction(Action writeAction, Action readAfterWriteAction) | |
| { | |
| this._lock.EnterUpgradeableReadLock(); | |
| try | |
| { | |
| this._lock.EnterWriteLock(); | |
| try | |
| { | |
| writeAction(); | |
| } | |
| finally | |
| { | |
| this._lock.ExitWriteLock(); | |
| } | |
| this._lock.EnterReadLock(); | |
| try | |
| { | |
| readAfterWriteAction(); | |
| } | |
| finally | |
| { | |
| this._lock.ExitReadLock(); | |
| } | |
| } | |
| finally | |
| { | |
| this._lock.ExitUpgradeableReadLock(); | |
| } | |
| } | |
| private TResult ReadWriteLockAction<TResult>(Func<TResult> readBeforeWriteAction, Action<TResult> writeAction, Action<TResult> readAfterWriteAction) | |
| { | |
| this._lock.EnterUpgradeableReadLock(); | |
| try | |
| { | |
| TResult obj = readBeforeWriteAction(); | |
| this._lock.EnterWriteLock(); | |
| try | |
| { | |
| writeAction(obj); | |
| } | |
| finally | |
| { | |
| this._lock.ExitWriteLock(); | |
| } | |
| this._lock.EnterReadLock(); | |
| try | |
| { | |
| readAfterWriteAction(obj); | |
| } | |
| finally | |
| { | |
| this._lock.ExitReadLock(); | |
| } | |
| return obj; | |
| } | |
| finally | |
| { | |
| this._lock.ExitUpgradeableReadLock(); | |
| } | |
| } | |
| #region IEnumerator support | |
| IEnumerator IEnumerable.GetEnumerator() => this.ReadLockAction(() => this._list.ToArray().GetEnumerator()); | |
| #endregion | |
| #region IEnumerator<T> support | |
| public IEnumerator<T> GetEnumerator() => this.ReadLockAction(() => ((IEnumerable<T>)this._list.ToArray()).GetEnumerator()); | |
| #endregion | |
| #region ICollection support | |
| bool ICollection.IsSynchronized => false; | |
| object ICollection.SyncRoot => null; | |
| void ICollection.CopyTo(Array array, int index) => this.CopyTo(array.Cast<T>().ToArray(), index); | |
| #endregion | |
| #region IList support | |
| bool IList.IsFixedSize => false; | |
| object IList.this[int index] | |
| { | |
| get { return this._list[index]; } | |
| set { this[index] = (T)value; } | |
| } | |
| bool IList.Contains(object value) => this.Contains((T)value); | |
| int IList.IndexOf(object value) => this.IndexOf((T)value); | |
| int IList.Add(object value) | |
| { | |
| this.Add((T)value); | |
| return _list.Count - 1; | |
| } | |
| void IList.Insert(int index, object value) => this.Insert(index, (T)value); | |
| void IList.Remove(object value) => this.Remove((T)value); | |
| #endregion | |
| #region IListEx support | |
| void IListEx.Move(object item, int newIndex) => this.Move((T)item, newIndex); | |
| void IListEx.Replace(object oldItem, object newItem) => this.Replace((T)oldItem, (T)newItem); | |
| void IListEx.ReplaceAt(int index, object newItem) => this.ReplaceAt(index, (T)newItem); | |
| #endregion | |
| #region IMultipleOperationList support | |
| void IMultipleOperationList.AddRange(IEnumerable items) => this.AddRange(items.Cast<T>()); | |
| void IMultipleOperationList.InsertRange(int startingIndex, IEnumerable items) => this.InsertRange(startingIndex, items.Cast<T>()); | |
| void IMultipleOperationList.ReplaceRangeAt(int oldStartingIndex, int length, IEnumerable items) => this.ReplaceRangeAt(oldStartingIndex, length, items.Cast<T>()); | |
| void IMultipleOperationList.ReplaceAll(IEnumerable items) => this.ReplaceAll(items.Cast<T>()); | |
| #endregion | |
| #region INotifyPropertyChanged support | |
| public event PropertyChangedEventHandler PropertyChanged; | |
| #endregion | |
| #region INotifyCollectionChanged support | |
| public event NotifyCollectionChangedEventHandler CollectionChanged; | |
| #endregion | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment