实例化泛型类型?

Son*_*Boy 3 .net c# oop generics collections

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

我遇到了一些问题.泛型集合if if表示抽象类的类型,我在实例化时遇到问题.

这是我的班级定义:

public class CacheCollection<T> : List<CacheItem<T>> where T : EntityBase
Run Code Online (Sandbox Code Playgroud)

这是我工作的部分完整的方法:

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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

Dan*_*Tao 7

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

现在问题是:如果类型不协变,则无法转换X<Derived>为a .也就是说,在你的情况下,仅仅因为派生并不意味着派生自.X<Base>X<T>TEntityBaseCacheCollection<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));
Run Code Online (Sandbox Code Playgroud)

幸运的是,围绕这个(在我看来)的方式是非常简单的.基本上,通过定义将派生非泛型基类CacheCollection<T>,与我的原始建议一致.更好的是,使用简单的非通用接口.

interface ICacheCollection
{
    EntityBase Item(int id);
}
Run Code Online (Sandbox Code Playgroud)

(请查看下面的更新代码,了解如何在通用类型中实现此接口).

然后,对于你的字典,而不是a Dictionary<Type, CacheCollection<EntityBase>>,将其定义为a 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这将允许您编写如下代码:

var cache = new GeneralCache();

RedEntity red = cache.GetOrAddItem<RedEntity>(1, id => new RedEntity(id));
BlueEntity blue = cache.GetOrAddItem<BlueEntity>(2, id => new BlueEntity(id));
Run Code Online (Sandbox Code Playgroud)

好吧,如果T派生自EntityBase但没有无参数构造函数,那么最好的办法是指定一个工厂方法,它将TCacheCollection<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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

(我也建议使用专用部件保持集合本身实现这个类,而不是从收集直接继承,如使用继承公开功能你可能想隐藏,如Add,Insert等)