using System; using System.Collections.Generic; namespace Boxfriend.Utils { /// /// Pools objects of type T, will create new objects as necessary /// public class ObjectPool where T : class { private readonly Stack _stack = new (); private readonly Func _objectCreator; private readonly Action _returnObjectToPool, _getObjectFromPool, _destroyObject; private readonly int _maxSize; /// /// Number of objects currently in the pool. /// public int Count => _stack.Count; /// Creates and returns an object of the specified type. /// Action called on object when pulled from the pool or created. /// Action called on object when returned to pool. /// Action called on object when it is to be destroyed. Can be null /// Number of objects to immediately add to the pool /// Maximum number of objects in the pool /// /// public ObjectPool (Func createObject, Action getObjectFromPool, Action returnObjectToPool, Action onDestroyObject = null, int defaultSize = 10, int maxSize = 100) { if (maxSize < defaultSize) throw new ArgumentOutOfRangeException(nameof(maxSize), "maxSize must be greater than or equal to defaultSize"); if (defaultSize < 0) throw new ArgumentOutOfRangeException(nameof(defaultSize), "defaultSize must be greater than or equal to 0"); _returnObjectToPool = returnObjectToPool ?? throw new ArgumentNullException(nameof(returnObjectToPool)); _getObjectFromPool = getObjectFromPool ?? throw new ArgumentNullException(nameof(getObjectFromPool)); _objectCreator = createObject ?? throw new ArgumentNullException(nameof(createObject)); _destroyObject = onDestroyObject; _maxSize = maxSize; for (var i = 0; i < defaultSize; i++) { ToPool(_objectCreator()); } } /// /// Gets an object from the pool or creates a new one if the pool is empty. Calls on the object /// public T FromPool () { var poolObject = _stack.Count > 0 ? _stack.Pop() : _objectCreator(); _getObjectFromPool(poolObject); return poolObject; } /// /// Adds an item to the pool and calls on it /// /// Item to be added public void ToPool (T item) { if (item == null) throw new ArgumentNullException(nameof(item)); _returnObjectToPool(item); if(_stack.Count >= _maxSize) { _destroyObject?.Invoke(item); return; } _stack.Push(item); } /// /// Removes all items from the pool, calling on it if not null. /// public void EmptyPool() { if(_destroyObject is null) { _stack.Clear(); return; } while(_stack.Count > 0) { var obj = _stack.Pop(); _destroyObject(obj); } } } }