// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // namespace MUnique.OpenMU.GameLogic; using System.Collections; using MUnique.OpenMU.PlugIns; using Nito.AsyncEx; /// /// A bucket, which can be observed for added and removed items. /// /// The type which should be hold by this bucket. public sealed class Bucket : IEnumerable { private readonly List _innerList; private readonly AsyncReaderWriterLock _locker = new(); /// /// Initializes a new instance of the class. /// /// The initial capacity. public Bucket(int capacity) { this._innerList = new List(capacity); } /// /// Occurs when an item has been added. /// public event AsyncEventHandler? ItemAdded; /// /// Occurs when an item has been removed. /// public event AsyncEventHandler? ItemRemoved; /// /// Gets the count. /// public int Count => this._innerList.Count; /// /// Adds the specified item. /// /// The item. public async ValueTask AddAsync(T item) { using (await this._locker.WriterLockAsync()) { this._innerList.Add(item); } if (this.ItemAdded is { } eventHandler) { await eventHandler(item).ConfigureAwait(false); } } /// /// Removes the specified item. /// /// The item. /// The success. public async ValueTask RemoveAsync(T item) { bool result; using (await this._locker.WriterLockAsync()) { result = this._innerList.Remove(item); } if (result && this.ItemRemoved is { } eventHandler) { await eventHandler(item).ConfigureAwait(false); } return result; } /// public IEnumerator GetEnumerator() { return new LockingEnumerator(this._locker, this._innerList); } /// IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } private sealed class LockingEnumerator : IEnumerator { private readonly AsyncReaderWriterLock _locker; private readonly IEnumerable _enumerable; private IEnumerator? _enumerator; private IDisposable? _lockRelease; public LockingEnumerator(AsyncReaderWriterLock locker, IEnumerable enumerable) { this._locker = locker; this._enumerable = enumerable; } public TEnumerated Current => this.Enumerator.Current; object IEnumerator.Current => this.Enumerator.Current!; private IEnumerator Enumerator { get { if (this._enumerator is { }) { return this._enumerator; } this._lockRelease = this._locker.ReaderLock(); return this._enumerator ??= this._enumerable.GetEnumerator(); } } public bool MoveNext() { return this.Enumerator.MoveNext(); } public void Reset() { this.Enumerator.Reset(); } public void Dispose() { this._enumerator?.Dispose(); this._lockRelease?.Dispose(); } } }