Skip to content

Instantly share code, notes, and snippets.

@mntone
Last active December 22, 2015 09:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mntone/ccb619bf66297569c8b2 to your computer and use it in GitHub Desktop.
Save mntone/ccb619bf66297569c8b2 to your computer and use it in GitHub Desktop.
// 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