Generic object pool
Asked Answered
A

3

1

Is it possible to create a generic object pool that creates new objects inside it? Plus it would be nice if this object creation could receive parameters.

    public interface IPoolable
    {
        void Dispose();
    }


    public class ObjectPool<T> where T : IPoolable
    {
        private List<T> pool;

        public T Get()
        {
            if(pool.count > 0)
            {
                return pool.Pop();
            }
            else
            {
                return new T(); //  <- How to do this properly?
            }
        }
    }

    public class SomeClass : IPoolable
    {
        int id;

        public SomeClass(int id)
        {
            this.id = id;
        }

        public void Dispose()
        {

        }
    }

    public class OtherClass : IPoolable
    {
        string name;
        int id;

        public OtherClass(string name, int id)
        {
            this.name = name;
            this.id = id;
        }

        public void Dispose()
        {

        }
    }

In a way that it can be used like this if it could receive parameters.

SomeClass a = myPool.Get(2);
OtherClass b = myOtherPool.Get("foo", 4);

Or this would also be fine if parameters were not possible.

SomeClass a = myPool.Get();
a.id = 2;
OtherClass b = myOtherPool.Get();
b.name = "foo";
b.id = 4;
Adis answered 22/6, 2016 at 19:39 Comment(2)
Are you basically talking about a factory? It's a common OO pattern. The pooling could be a component of it, but, in general, instantiating an object is extremely quick and pooling would be unnecessary unless there's a lot of object setup to be done in the constructor. The only way to make it generic really would be to use reflection to instantiate the objects.Epimorphosis
He is talking about maintaining a pool of already created objects and only creating a new instance if the pool is empty, kind of like how connection pooling works only generic instead of just connections. I agree though, for light objects it is a bit of a waste.Excursive
D
5

You can use the Activator.CreateInstance Method

public static object CreateInstance(
    Type type,
    params object[] args
)

Like this

return (T)Activator.CreateInstance(typeof(T), id);

There is, however, no way to specify that a type must provide a constructor with an argument; neither in an interface declaration, nor in the generic type constraints, nor with class inheritance.

Dentoid answered 22/6, 2016 at 19:47 Comment(0)
U
3

I was looking for something similar and came across this:

public class ObjectPool<TObject>
    {
        private int maxPoolSize;
        private SpinLock poolLock;
        private Dictionary<Type, Stack<TObject>> poolCache;
        private Func<TObject> factory;

        public ObjectPool(int poolSize)
        {
            this.maxPoolSize = poolSize;
            this.poolLock = new SpinLock(false);
            this.poolCache = new Dictionary<Type, Stack<TObject>>();
        }

        public ObjectPool(int poolSize, Func<TObject> factory) : this(poolSize)
        {
            this.factory = factory;
        }

        public T Rent<T>() where T : TObject
            => (T)this.Rent(typeof(T));

        public TObject Rent(Type type)
        {
            bool lockTaken = false;
            Stack<TObject> cachedCollection;
            this.poolLock.Enter(ref lockTaken);

            try
            {
                if (!this.poolCache.TryGetValue(type, out cachedCollection))
                {
                    cachedCollection = new Stack<TObject>();
                    this.poolCache.Add(type, cachedCollection);
                }
            }
            finally
            {
                if (lockTaken)
                {
                    this.poolLock.Exit(false);
                }
            }

            if (cachedCollection.Count > 0)
            {
                TObject instance = cachedCollection.Pop();
                if (instance != null)
                    return instance;
            }

            // New instances don't need to be prepared for re-use, so we just return it.
            if (this.factory == null)
            {
                return (TObject)Activator.CreateInstance(type);
            }
            else
            {
                return this.factory();
            }
        }

        public void Return(TObject instanceObject)
        {
            Stack<TObject> cachedCollection = null;
            Type type = typeof(TObject);

            bool lockTaken = false;
            this.poolLock.Enter(ref lockTaken);
            try
            {
                if (!this.poolCache.TryGetValue(type, out cachedCollection))
                {
                    cachedCollection = new Stack<TObject>();
                    this.poolCache.Add(type, cachedCollection);
                }

                if (cachedCollection.Count >= this.maxPoolSize)
                {
                    return;
                }

                cachedCollection.Push(instanceObject);
            }
            finally
            {
                if (lockTaken)
                {
                    this.poolLock.Exit(false);
                }
            }
        }
    }

It's a very nice implementation that I shamelessly stole from here

It supports pooling of any objects that inherit from the generic type parameter.

Unsuspecting answered 26/7, 2018 at 6:13 Comment(0)
E
2

You could do something like this:

public class ObjectPool<T>
{
    private Queue<T> _pool = new Queue<T>();

    private const int _maxObjects = 100;  // Set this to whatever

    public T Get(params object[] parameters)
    {
        T obj;

        if (_pool.Count < 1)
            obj = (T)Activator.CreateInstance(typeof(T), parameters);
        else
            obj = _pool.Dequeue();

        return obj;
    }

    public void Put(T obj)
    {
        if (_pool.Count < _maxObjects)
            _pool.Enqueue(obj);
    }
}
Epimorphosis answered 22/6, 2016 at 19:48 Comment(1)
That will do. Thanks!Adis

© 2022 - 2024 — McMap. All rights reserved.