实例化通用类型? [英] Instantiating a generic type?

查看:101
本文介绍了实例化通用类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我遇到了一些问题。



这是我的类定义:

  public class CacheCollection< T> :List< CacheItem< T>其中T:EntityBase 

这里是我正在工作的部分完成的方法:

  public T Item(int Id)
{
CacheItem< T> result = this.Where(t => t.Entity.Id == Id).First();

if(result == null)//项目尚未在缓存中,加载它!
{
T entity = new T(Id); //设计时间异常这里:不能创建变量类型'T'的实例,因为它没有new()约束
result = new CacheItem< T>(entity);
this.Add(result);
}

return result.Entity;
}

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



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

解决方案

strong> UPDATE 2 :你在注释中说你没有定义一个非通用的 CacheCollection 类型;但是你继续说你有 Dictionary< Type,CacheCollection> 。这些语句不能都是真的,所以我猜测 CacheCollection 你的意思是 CacheCollection< EntityBase>



现在的问题是: X< Derived> 不能转换为 X< Base> 如果 X< T> 键入不是协变。也就是说,在你的情况下,只是因为 T 派生自 EntityBase 并不意味着 CacheCollection< ; T> 源自 CacheCollection< EntityBase>



为什么是这样,考虑 List< T> 类型。假设您有列表< string> 列表< object> string 源自 object ,但不跟随 List< string> 源自 List< object> ;如果是,那么你可以这样代码:

  var strings = new List< string>(); 

//如果这个转换是可能的...
var objects =(List< object>)strings;

// ... crap!那么你可以添加一个DateTime到List< string> ;!
objects.Add(new DateTime(2010,8,23));幸运的是,(在我看来)这个方法是相当简单的。基本上,通过定义非泛型基类, CacheCollection< T> 将派生,与我的原始建议。更好的是,使用一个简单的非通用接口。

  interface ICacheCollection 
{
EntityBase Item int id);
}

(请参阅下面的更新代码,了解如何实现



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



< hr>

UPDATE :似乎您是从我们扣缴的!所以你有一个非泛型的 CacheCollection 基类, CacheCollection< T> p>

如果我理解你对这个答案的最新评论是正确的,这里是我的建议。写一个类来提供对词典<类型,缓存集合> 的间接访问。这样,您可以拥有许多 CacheCollection< T> 实例,而不会牺牲类型安全性。



> note:基于新的更新代码修改):

  class GeneralCache 
{
private Dictionary< Type,ICacheCollection> _collections;

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

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

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

CacheCollection< T> strongTyped =(CacheCollection< T>)集合;
return strongTyped.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 构造函数中的适当参数。



像这样(注意:根据更新修改代码):

  public class CacheCollection< T> :List< CacheItem< T>>,ICacheCollection其中T:EntityBase 
{
private Func _厂;

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

//这里你可以定义Item方法返回一个更具体的类型
//比ICacheCollection接口所需要的类型。这是通过在下面明确定义接口来实现
//。
public T Item(int id)
{
//注意:使用FirstOrDefault,因为First会抛出异常
//如果项目不存在。
CacheItem< T> result = this.Where(t => t.Entity.Id == id)
.FirstOrDefault();

if(result == null)//项目尚未在缓存中,加载它!
{
T entity = _factory(id);

//注意:在这种情况下,您似乎忘了实例化结果变量
//。
result = new CacheItem< T>(entity);

Add(result);
}

return result.Entity;
}

//这里你明确实现ICacheCollection接口;
//这有效地隐藏了这个方法的接口签名,而
//使用更具体的返回类型来暴露另一个签名。
EntityBase ICacheCollection.Item(int id)
{
//这将调用方法的公共版本。
return Item(id);
}
}

我也建议,如果你的项目要去具有唯一ID,使用字典< int,CacheItem< T>< / code>作为后备存储,而不是 List< CacheItem< T> ; ,因为它将使您的项目查找O(1)而不是O(N)。



(我也建议实现这个类使用一个私有成员保存集合本身而不是直接从集合继承,因为使用继承暴露你可能想隐藏的功能,例如添加插入等)


I'm currently working on a generic collection class and I'd like to create a method which returns an object from the collection. If the specific object does not exist in the collection then the object should be created, added to the collection, and then returned.

I'm encountering a few problems though. The generic collection if of a type which represents an abstract class and I'm having trouble instantiating that.

Here's my class definition:

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

And here's the partially complete method on which I am working:

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;
}

Any ideas on how to get around this?

EDIT: All classes derived from EntityBase have Id as a read-only property.

解决方案

UPDATE 2: Well, you say in a comment that you have not defined a non-generic CacheCollection type; but then you go on to say that you have a Dictionary<Type, CacheCollection>. These statements cannot both be true, so I am guessing that by CacheCollection you mean CacheCollection<EntityBase>.

Now here's the problem: a X<Derived> cannot be cast to a X<Base> if the X<T> type is not covariant. That is, in your case, just because T derives from EntityBase does not mean that CacheCollection<T> derives from CacheCollection<EntityBase>.

For a concrete illustration of why this is, consider the List<T> type. Say you have a List<string> and a List<object>. string derives from object, but it does not follow that List<string> derives from List<object>; if it did, then you could have code like this:

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

Fortunately, the way around this (in my view) is pretty straightforward. Basically, go with my original suggestion by defining a non-generic base class from which CacheCollection<T> will derive. Better yet, go with a simple non-generic interface.

interface ICacheCollection
{
    EntityBase Item(int id);
}

(Take a look at my updated code below to see how you can implement this interface in your generic type).

Then for your dictionary, instead of a Dictionary<Type, CacheCollection<EntityBase>>, define it as a Dictionary<Type, ICacheCollection> and the rest of your code should come together.


UPDATE: It seems that you were withholding from us! So you have a non-generic CacheCollection base class from which CacheCollection<T> derives, am I right?

If my understanding of your latest comment to this answer is correct, here's my advice to you. Write a class to provide indirect access to this Dictionary<Type, CacheCollection> of yours. This way you can have many CacheCollection<T> instances without sacrificing type safety.

Something like this (note: code modified based on new update above):

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

This would allow you to write code like the following:

var cache = new GeneralCache();

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


Well, if T derives from EntityBase but does not have a parameterless constructor, your best bet is going to be to specify a factory method that will generate a T for the appropriate parameters in your CacheCollection<T> constructor.

Like this (note: code modified based on new update above):

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

I would also recommend, if your items are going to have unique IDs, to use a Dictionary<int, CacheItem<T>> as your backing store instead of a List<CacheItem<T>> as it will make your item lookup O(1) instead of O(N).

(I would also recommend implementing this class using a private member to hold the collection itself rather than inheriting from the collection directly, as using inheritance exposes functionality you probably want hidden such as Add, Insert, etc.)

这篇关于实例化通用类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆