创建一个通用类池,在其中提供泛型参数并获取使用该参数的通用对象

时间:2012-01-19 12:41:41

标签: c# generics object-pooling generic-type-argument

目标

我有一个泛型类GenericClass<T>,我想要汇集实例 我很想知道我是否可以获得语法:

MyGenericPool = new GenericPool<GenericClass>();
// Or maybe it's MyGenericPool = new GenericPool<GenericClass<>>();

GenericClass<TGenericParam> GenericClassInstance =
    MyGenericPool.Get<TGenericParam>();

(我对泛型的理解说,不,我不能,不要愚蠢的语法不存在/不会起作用,但我对其他人的想法很感兴趣。)


显示我的工作

我有点怀疑,因为根据我的理解,GenericClass<string>GenericClass<int>类型与类型系统的观点并不真正相关。

现在,我意识到我可以接近,即:

GenericClass<TGenericParam> GenericClassInstance =
    GenericPool.Get<GenericClass<TGenericParam>>();

然后让GenericPool只在某个地方存储Dictionary<Type, ObjectPool<object>> 我很想知道我是否可以避免这样做。我不想每次都要指定泛型类型,作为调用者,我只更改泛型类型参数。我也希望能够强制执行(编译时)所有进入我的GenericObjectPool<T>的对象属于集合泛型类型(T<>)。


我认为问题源于无法将泛型类型参数视为通用类型参数。如果我能做到(我可以吗?)那么下面的内容可能会起作用:

public class GenericClassPool<TGeneric> where TGeneric : class<>
{
    private readonly Dictionary<Type, object> objectPools = new Dictionary<Type, object>();


    private void EnsureObjectPoolExists<TGenericParam>()
    {
        if (!objectPools.ContainsKey(typeof(TGenericParam)))
        {
            objectPools.Add(typeof(TGenericParam), new ObjectPool<TGeneric<TGenericParam>>(() => Activator.CreateInstance(typeof(TGeneric<TGenericParam>)) as TGeneric<TGenericParam>));
        }
    }

    private ObjectPool<TGeneric<TGenericParam>> GetPool<TGenericParam>()
    {
        EnsureObjectPoolExists<TGenericParam>();
        return (objectPools[typeof(TGenericParam)] as ObjectPool<TGeneric<TGenericParam>>);
    }

    public void Add<TTypeParam>(TGeneric<TGenericParam> obj)
    {
        EnsureObjectPoolExists<TTypeParam>();

        GetPool<TGenericParam>().Add(obj);
    }

    public TGeneric<TGenericParam> Get<TGenericParam>()
    {
        return GetPool<TGenericParam>().Get() as TGeneric<TGenericParam>;
    }
}

问题

我可以获得我想要的语法(在顶部)吗?如果没有,我能接近多少?

2 个答案:

答案 0 :(得分:4)

您尝试实现的解决方案/语法不起作用,因为如果没有类型参数作为另一个泛型类型的类型参数,则不能使用泛型类型。

但是,您可以通过以下方法获得类似的结果:

  1. 为类池创建一个基类,要求您提供完整的泛型类型
  2. 为特定泛型类型
  3. 创建派生类

    类似的东西:

    public class ObjectPool
    {
        Dictionary<Type, object> _objectPool = new Dictionary<Type, object>();
    
        public void Add<TKey, TValue>(TValue value)
        {
            _objectPool.Add(typeof(TKey), value);
        }
    
        public TValue Get<TKey, TValue>() where TValue : class
        {
            object value;
            if(_objectPool.TryGetValue(typeof(TKey), out value))
                return value as TValue;
            return null;
        }
    }
    
    public class GenericClassPool : ObjectPool
    {
        public void Add<TGenericParam>(GenericClass<TGenericParam> obj)
        {
            Add<TGenericParam, GenericClass<TGenericParam>>(obj);
        }
    
        public GenericClass<TGenericParam> Get<TGenericParam>()
        {
            return Get<TGenericParam, GenericClass<TGenericParam>>();
        }
    }
    

    用法如下:

    var pool = new GenericClassPool();
    pool.Add(new GenericClass<string> { Property = "String" });
    pool.Add(new GenericClass<int> { Property  = 0 });
    
    GenericClass<string> firstObject = pool.Get<string>();
    GenericClass<int> secondObject = pool.Get<int>();
    

    此解决方案的缺点是您需要为要池的每个泛型类型创建一个池类,因此您可能会从<className>Pool派生出很多ObjectPool个类。
    为了使其可用,所有实际代码都需要在ObjectPool类中,并且只有提供泛型参数的代码仍保留在派生类中。

答案 1 :(得分:0)

我想分享自己的游泳池课程。它们与发布的其他代码具有类似的API,但在我的完全偏见的意见中更加开发和灵活。

单一类型对象池:

/// <summary>
/// Allows code to operate on a Pool<T> without casting to an explicit generic type.
/// </summary>
public interface IPool
{
    Type ItemType { get; }
    void Return(object item);
}

/// <summary>
/// A pool of items of the same type.
/// 
/// Items are taken and then later returned to the pool (generally for reference types) to avoid allocations and
/// the resulting garbage generation.
/// 
/// Any pool must have a way to 'reset' returned items to a canonical state.
/// This class delegates that work to the allocator (literally, with a delegate) who probably knows more about the type being pooled.
/// </summary>    
public class Pool<T> : IPool
{
    public delegate T Create();
    public readonly Create HandleCreate;

    public delegate void Reset(ref T item);
    public readonly Reset HandleReset;

    private readonly List<T> _in;

#if !SHIPPING
    private readonly List<T> _out;
#endif

    public Type ItemType
    {
        get
        {
            return typeof (T);   
        }            
    }

    public Pool(int initialCapacity, Create createMethod, Reset resetMethod)
    {
        HandleCreate = createMethod;
        HandleReset = resetMethod;

        _in = new List<T>(initialCapacity);            
        for (var i = 0; i < initialCapacity; i++)
        {
            _in.Add(HandleCreate());
        }
#if !SHIPPING
        _out = new List<T>();            
#endif
    }

    public T Get()
    {
        if (_in.Count == 0)
        {
            _in.Add(HandleCreate());
        }

        var item = _in.PopLast();
#if !SHIPPING
        _out.Add(item);
#endif
        return item;
    }

    public void Return( T item )
    {
        HandleReset(ref item);
#if !SHIPPING
        Debug.Assert(!_in.Contains(item), "Returning an Item we already have.");
        Debug.Assert(_out.Contains(item), "Returning an Item we never gave out.");
        _out.Remove(item);
#endif
        _in.Add(item);
    }

    public void Return( object item )
    {
        Return((T) item);
    }

#if !SHIPPING
    public void Validate()
    {
        Debug.Assert(_out.Count == 0, "An Item was not returned.");
    }
#endif
}

接下来是一个多类型的池。

使用此类或自己使用多个Pool<T>之间没有区别。但在某些情况下使用此类会使代码看起来更清晰,即。消除if/else (type == foo)块。

/// <summary>
/// Represents a collection of pools for one or more object types.
/// </summary>
public class Pooler
{
    private readonly List<IPool> _pools;

    public Pooler()
    {
        _pools = new List<IPool>();
    }

    public void DefineType<T>(int initialCapacity, Pool<T>.Create createHandler, Pool<T>.Reset resetHandler)
    {
        var p = new Pool<T>(initialCapacity, createHandler, resetHandler);
        _pools.Add(p);
    }

    public T Get<T>()
    {
        var p = GetPool(typeof (T));
        if (p == null)
            throw new Exception(string.Format("Pooler.Get<{0}>() failed; there is no pool for that type.", typeof(T)));

        return ((Pool<T>)p).Get();
    }

    public void Return(object item)
    {
        var p = GetPool(item.GetType());
        if (p == null)
            throw new Exception(string.Format("Pooler.Get<{0}>() failed; there is no pool for that type.", item.GetType()));

        p.Return(item);            
    }

    private IPool GetPool(Type itemType)
    {
        foreach (var p in _pools)
        {
            if (p.ItemType == itemType)
            {
                return p;
            }
        }

        return null;
    }
}

就每次访问池时不必指定类型参数而言,我经常为经常使用的特定类型声明一个具体的池。

public class GameObjectPool : Pool<GameObject>
{   
   public GameObjectPool(int initialCapacity)
      :base(initialCapacity, CreateObject, ResetObject)
   {
   }

   private GameObject CreateObject()
   { ... }

   private GameObject ResetObject()
   { ... }
}

然后你的代码是......

_pool = new Pool<GameObject>(10);
var obj = _pool.Get<GameObject>();

可以成为......

_pool = new GameObjectPool(10);
var obj = _pool.Get();

另一种选择是......

using GameObjectPool=MyRootnamespace.Pool<GameObject>

如果您对池有大量引用,但它们都在同一个代码文件中,那么它可以工作。