我目前正在研究一个通用集合类,我想创建一个从集合中返回一个对象的方法。如果集合中不存在特定对象,则应创建该对象,将其添加到集合中,然后返回。

不过我遇到了一些问题。泛型集合 if 是代表抽象类的类型,我无法实例化它。

这是我的类定义:

public class CacheCollection<T> : List<CacheItem<T>> where T : EntityBase

这是我正在使用的部分完整的方法:
public T Item(int Id)
{
    CacheItem<T> result = this.Where(t => t.Entity.Id == Id).First();

    if (result == null) //item not yet in cache, load it!
    {
        T entity = new T(Id); //design time exception here: Cannot create an instance of the variable type 'T' because it does not have the new() constraint
        result = new CacheItem<T>(entity);
        this.Add(result);
    }

    return result.Entity;
}

关于如何解决这个问题的任何想法?

编辑:从 EntityBase 派生的所有类都将 Id 作为只读属性。

最佳答案

更新 2 :好吧,您在评论中说您没有定义非通用 CacheCollection 类型;但是你接着说你有一个 Dictionary<Type, CacheCollection> 。这些陈述不可能都是真的,所以我猜 CacheCollection 你的意思是 CacheCollection<EntityBase>

现在问题来了:如果 X<Derived> 类型为 is not covariant ,则无法将 X<Base> 转换为 X<T> 。也就是说,在您的情况下,仅仅因为 T 派生自 EntityBase 并不意味着 CacheCollection<T> 派生自 CacheCollection<EntityBase>

要具体说明原因,请考虑 List<T> 类型。假设您有一个 List<string> 和一个 List<object>string 派生自 object ,但不遵循 List<string> 派生自 List<object> ;如果是这样,那么你可以有这样的代码:

var strings = new List<string>();

// If this cast were possible...
var objects = (List<object>)strings;

// ...crap! then you could add a DateTime to a List<string>!
objects.Add(new DateTime(2010, 8, 23));

幸运的是,解决这个问题的方法(在我看来)非常简单。基本上,通过定义一个 非泛型基类,CacheCollection<T> 将从中派生 来遵循我最初的建议。更好的是,使用简单的非通用接口(interface)。
interface ICacheCollection
{
    EntityBase Item(int id);
}

(看看下面我更新的代码,看看如何在你的泛型类型中实现这个接口(interface))。

然后对于您的字典,而不是 Dictionary<Type, CacheCollection<EntityBase>> ,将其定义为 Dictionary<Type, ICacheCollection> 并且您的其余代码应该放在一起。

更新 :您似乎对我们隐瞒了!所以你有一个 CacheCollection 派生自的非通用 CacheCollection<T> 基类,对吗?

如果我对你对这个答案的最新评论的理解是正确的,这是我给你的建议。编写一个类来提供对您的这个 Dictionary<Type, CacheCollection> 的间接访问。通过这种方式,您可以拥有许多 CacheCollection<T> 实例而不会牺牲类型安全性。

像这样( 注意:根据 上面的新更新修改的代码):
class GeneralCache
{
    private Dictionary<Type, ICacheCollection> _collections;

    public GeneralCache()
    {
        _collections = new Dictionary<Type, ICacheCollection>();
    }

    public T GetOrAddItem<T>(int id, Func<int, T> factory) where T : EntityBase
    {
        Type t = typeof(T);

        ICacheCollection collection;
        if (!_collections.TryGetValue(t, out collection))
        {
            collection = _collections[t] = new CacheCollection<T>(factory);
        }

        CacheCollection<T> stronglyTyped = (CacheCollection<T>)collection;
        return stronglyTyped.Item(id);
    }
}

这将允许您编写如下代码:
var cache = new GeneralCache();

RedEntity red = cache.GetOrAddItem<RedEntity>(1, id => new RedEntity(id));
BlueEntity blue = cache.GetOrAddItem<BlueEntity>(2, id => new BlueEntity(id));

好吧,如果 T 派生自 EntityBase 但没有无参数构造函数,那么最好的办法是指定一个工厂方法,该方法将为 T 构造函数中的适当参数生成 CacheCollection<T>

像这样( 注意:基于 上面的新更新修改的代码):
public class CacheCollection<T> : List<CacheItem<T>>, ICacheCollection where T : EntityBase
{
    private Func<int, T> _factory;

    public CacheCollection(Func<int, T> factory)
    {
        _factory = factory;
    }

    // Here you can define the Item method to return a more specific type
    // than is required by the ICacheCollection interface. This is accomplished
    // by defining the interface explicitly below.
    public T Item(int id)
    {
        // Note: use FirstOrDefault, as First will throw an exception
        // if the item does not exist.
        CacheItem<T> result = this.Where(t => t.Entity.Id == id)
            .FirstOrDefault();

        if (result == null) //item not yet in cache, load it!
        {
            T entity = _factory(id);

            // Note: it looks like you forgot to instantiate your result variable
            // in this case.
            result = new CacheItem<T>(entity);

            Add(result);
        }

        return result.Entity;
    }

    // Here you are explicitly implementing the ICacheCollection interface;
    // this effectively hides the interface's signature for this method while
    // exposing another signature with a more specific return type.
    EntityBase ICacheCollection.Item(int id)
    {
        // This calls the public version of the method.
        return Item(id);
    }
}

我还建议,如果您的项目将具有唯一 ID,请使用 Dictionary<int, CacheItem<T>> 作为后备存储而不是 List<CacheItem<T>>,因为它会使您的项目查找 O(1) 而不是 O(N)。

(我还建议使用私有(private)成员来实现这个类来保存集合本身,而不是直接从集合继承,因为使用继承会暴露您可能想要隐藏的功能,例如 AddInsert 等)

关于c# - 实例化泛型类型?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/3550173/

10-13 06:47