using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using UnityEngine; namespace UnityGameEngine.Utilities { public class ObjectPoolWithConstructorInternal<T> { public int ObjectsCreated { get; internal set; } public int InUse { get; internal set; } public int Available { get { return ObjectsCreated - InUse; } } private Type objectType; private ConstructorInfo constructor; private int numConstructorArgs = 0; private Queue<T> availableQueue = new Queue<T>(); private HashSet<T> availableObjects = new HashSet<T>(); public ObjectPoolWithConstructorInternal(Type objectType, Type[] constructorParams) { this.objectType = objectType; ConstructorInfo constructor = objectType.GetConstructor(constructorParams); if (!typeof(T).IsAssignableFrom(objectType)) { Debug.LogError("Can't create an ObjectPool<" + typeof(T).Name + "> with a constructor from " + objectType.Name); } else if (constructor == null) { string err = "Can't construct an ObjectPool<" + objectType.Name + "> with constructor params ("; err += String.Join(", ", constructorParams.Select(t => t.Name).ToArray()) + "): No such constructor exists!"; Debug.LogError(err); } else { this.constructor = constructor; numConstructorArgs = constructorParams.Length; } } public ObjectPoolWithConstructorInternal(Type[] constructorParams) : this(typeof(T), constructorParams) { } protected bool HasAvailableObject { get { return availableObjects.Count > 0; } } protected T GetObjectIfAvailable() { InUse++; if (availableObjects.Count > 0) { T obj = availableQueue.Dequeue(); while (!availableObjects.Remove(obj)) obj = availableQueue.Dequeue(); return obj; } return default(T); } protected T GetNewObject(object[] constructorArgs) { ObjectsCreated++; InUse++; if (constructorArgs.Length != numConstructorArgs) { Debug.LogError("ObjectPool<" + objectType.Name + ">: not enough arguments provided!"); return default(T); } bool valid = true; object obj = null; if (constructor == null) return default(T); try { obj = constructor.Invoke(constructorArgs); if (!(obj is T)) { valid = false; } } catch (Exception e) { valid = false; } if (!valid) { Debug.LogError("ObjectPool<" + objectType.Name + ">: Incorrect constructor parameters passsed!"); return default(T); } return (T)obj; } public void ReturnObject(T obj) { if (!availableObjects.Contains(obj)) { availableQueue.Enqueue(obj); availableObjects.Add(obj); InUse--; } } public int AvailableObjectsRemoved { get; private set; } public T RemoveAvailable() { if (availableObjects.Count > 0) { T obj = availableQueue.Dequeue(); while (!availableObjects.Remove(obj)) obj = availableQueue.Dequeue(); ObjectsCreated--; AvailableObjectsRemoved++; return obj; } return default(T); } public void RemoveAvailable(T obj) { if (availableObjects.Contains(obj)) { availableObjects.Remove(obj); ObjectsCreated--; AvailableObjectsRemoved++; } } } public abstract class ObjectPoolBase<T> { public int ObjectsCreated { get; internal set; } public int InUse { get; internal set; } public int Available { get { return ObjectsCreated - InUse; } } private Queue<T> availableQueue = new Queue<T>(); private HashSet<T> availableObjects = new HashSet<T>(); private ConstructorInfo constructor; public ObjectPoolBase(Type newType = null) { if (newType != null) { constructor = newType.GetConstructor(Type.EmptyTypes); } } protected abstract T CreateNewObject(); public T GetNextObject() { InUse++; T obj = default(T); if (availableObjects.Count > 0) { obj = availableQueue.Dequeue(); while (!availableObjects.Remove(obj)) obj = availableQueue.Dequeue(); } else { ObjectsCreated++; obj = CreateNewObject(); } return obj; } public void ReturnObject(T obj) { if (!availableObjects.Contains(obj)) { InUse--; availableObjects.Add(obj); availableQueue.Enqueue(obj); } } public void ReturnObject(object obj) { if (obj is T) { if (!availableObjects.Contains((T)obj)) { InUse--; availableObjects.Add((T)obj); availableQueue.Enqueue((T)obj); } } } public int AvailableObjectsRemoved { get; private set; } public T RemoveAvailable() { if (availableObjects.Count > 0) { T obj = availableQueue.Dequeue(); while (!availableObjects.Remove(obj)) obj = availableQueue.Dequeue(); ObjectsCreated--; AvailableObjectsRemoved++; return obj; } return default(T); } public void RemoveAvailable(T obj) { if (availableObjects.Contains(obj)) { availableObjects.Remove(obj); ObjectsCreated--; AvailableObjectsRemoved++; } } } public class ObjectPool<T> : ObjectPoolBase<T> where T : new() { protected override T CreateNewObject() { return new T(); } } public class ObjectPoolBaseType<T> : ObjectPoolBase<T> { private ConstructorInfo constructor; public ObjectPoolBaseType(Type type) { constructor = type.GetConstructor(Type.EmptyTypes); } protected override T CreateNewObject() { return (T)constructor.Invoke(null); } } public class ObjectPool<T, TConstructArg1> : ObjectPoolWithConstructorInternal<T> { public ObjectPool() : base(new Type[] { typeof(TConstructArg1) }) { } public ObjectPool(Type objectType) : base(objectType, new Type[] { typeof(TConstructArg1) }) { } public T GetNextObject(TConstructArg1 arg1) { if (HasAvailableObject) { return GetObjectIfAvailable(); } return GetNewObject(new object[] { arg1 }); } } public class ObjectPool<T, TConstructArg1, TConstructArg2> : ObjectPoolWithConstructorInternal<T> { public ObjectPool() : base(new Type[] { typeof(TConstructArg1), typeof(TConstructArg2) }) { } public ObjectPool(Type objectType) : base(objectType, new Type[] { typeof(TConstructArg1), typeof(TConstructArg2) }) { } public T GetNextObject(TConstructArg1 arg1, TConstructArg2 arg2) { if (HasAvailableObject) { return GetObjectIfAvailable(); } return GetNewObject(new object[] { arg1, arg2 }); } } public class ObjectPool<T, TConstructArg1, TConstructArg2, TConstructArg3> : ObjectPoolWithConstructorInternal<T> { public ObjectPool() : base(new Type[] { typeof(TConstructArg1), typeof(TConstructArg2), typeof(TConstructArg3) }) { } public ObjectPool(Type objectType) : base(objectType, new Type[] { typeof(TConstructArg1), typeof(TConstructArg2), typeof(TConstructArg3) }) { } public T GetNextObject(TConstructArg1 arg1, TConstructArg2 arg2, TConstructArg3 arg3) { if (HasAvailableObject) { return GetObjectIfAvailable(); } return GetNewObject(new object[] { arg1, arg2, arg3 }); } } }