3

目标

我有一个通用类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>;
    }
}

问题

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

4

2 回答 2

4

您尝试实现的解决方案/语法不能以这种方式工作,因为您不能使用没有其类型参数的泛型类型作为另一个泛型类型的类型参数。

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

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

像这样的东西:

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>PoolObjectPool.
为了使其可用,所有实际代码都需要在ObjectPool类中,并且只有提供泛型参数的代码保留在派生类中。

于 2012-01-19T13:48:17.167 回答
1

我想分享我自己的泳池课程。在我完全有偏见的观点中,它们具有与发布的其他代码类似的 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>

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

于 2014-07-31T22:32:07.380 回答