创建一个泛型类池,在其中给定泛型参数并获得使用该参数的泛型对象

本文关键字:泛型 参数 对象 一个 泛型类 在其中 创建 | 更新日期: 2023-09-27 18:20:58

目标

我有一个泛型类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>>();

然后使CCD_ 4仅在某处存储CCD_
我很想看看是否可以避免这样做。我不想每次作为调用者只更改泛型类型参数时都指定泛型类型我还希望能够强制(编译时)进入我的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>;
    }
}

问题

我能得到我想要的语法吗(在顶部)?如果没有,我能走多远?

创建一个泛型类池,在其中给定泛型参数并获得使用该参数的泛型对象

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

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

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

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

我想共享我自己的池类。它们与发布的其他代码有类似的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>

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