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);
}
}
}
}