Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/cmake/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 实例化泛型类型?_C#_.net_Generics_Oop_Collections - Fatal编程技术网

C# 实例化泛型类型?

C# 实例化泛型类型?,c#,.net,generics,oop,collections,C#,.net,Generics,Oop,Collections,我目前正在处理一个泛型集合类,我想创建一个从集合中返回对象的方法。如果集合中不存在特定对象,则应创建该对象,将其添加到集合中,然后返回 不过我遇到了一些问题。泛型集合是一个表示抽象类的类型,我在实例化它时遇到了问题 以下是我的类定义: public class CacheCollection<T> : List<CacheItem<T>> where T : EntityBase 公共类CacheCollection:列表,其中T:EntityBase 下

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

不过我遇到了一些问题。泛型集合是一个表示抽象类的类型,我在实例化它时遇到了问题

以下是我的类定义:

public class CacheCollection<T> : List<CacheItem<T>> where T : EntityBase
公共类CacheCollection:列表,其中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;
}
公共T项(内部Id)
{
CacheItem result=this.Where(t=>t.Entity.Id==Id).First();
如果(result==null)//项尚未在缓存中,请加载它!
{
T entity=new T(Id);//此处的设计时异常:无法创建变量类型“T”的实例,因为它没有new()约束
结果=新缓存项(实体);
这个。添加(结果);
}
返回结果。实体;
}
有什么办法可以解决这个问题吗


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

更新2:好的,您在注释中说您没有定义非泛型的
CacheCollection
类型;但是你接着说你有一本
字典
。这些陈述不可能都是真的,所以我猜
CacheCollection
的意思是
CacheCollection

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

<> >为了具体说明这是为什么,请考虑<代码>列表>代码>类型。假设您有一个
列表
和一个
列表
<代码>字符串派生自
对象
,但它并不表示
列表
派生自
列表
;如果有,那么您可以使用如下代码:

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
基类,它是
CacheCollection
派生的,对吗

如果我对你对这个答案的最新评论的理解是正确的,这里是我给你的建议。编写一个类来间接访问您的
词典
。通过这种方式,您可以在不牺牲类型安全性的情况下拥有许多
CacheCollection
实例

类似这样的内容(注意:根据上面的新更新修改代码)

我还建议,如果您的项目将具有唯一ID,请使用
字典
作为备份存储,而不是
列表
,因为它将使您的项目查找成为O(1)而不是O(N)


(我还建议使用私有成员来保存集合本身而不是直接从集合继承来实现此类,因为使用继承会公开您可能希望隐藏的功能,例如
添加
插入
,等等)。

目前没有任何东西阻止您(或其他人)声明
CacheCollection
的实例,其中
T
不能直接创建(例如,如果
T
表示抽象类)。根据错误提示,您可以通过向通用参数添加
new()
约束,要求
T
是“可创建的”:

public class CacheCollection<T> : List<CacheItem<T>> where T : EntityBase, new()

我相信你得到的编译器错误告诉你解决方法。您必须添加另一个类型约束,以便它知道您可以创建实例。您只需添加
new()

但是,在运行时也不能保证这一点,除非您跟上所有子类实现的进度

有两件事:

// add new() to your where clause:
public class CacheCollection<T> : List<CacheItem<T>> where T : EntityBase, new()

// change the creation of the object:
...
T entity = new T();
entity.Id = Id;
...
//将new()添加到where子句:
公共类CacheCollection:列表,其中T:EntityBase,new()
//更改对象的创建:
...
T实体=新的T();
实体Id=Id;
...

我在类似情况下所做的就是创建一个IId接口

public interface IId
{
   public Id { get; set; }
}
通过使用分部类,很容易将接口声明添加到实体类中:

public partial MyClass : IId { }
您的类定义现在变为:

public class CacheCollection<T> : List<CacheItem<T>> where T : EntityBase, IId, new()
公共类CacheCollection:列表,其中T:EntityBase,IId,new()
然后将
T entity=newt{Id=Id}帮助?(在这种情况下我从未尝试过)没有。如果
Id
是只读属性,Dan Tao的建议可能是最简单、最安全的解决方案。对底部的建议投了赞成票。我不确定我是否喜欢这个解决方案中的工厂。谢谢,丹。这显然是在正确的轨道上。不过,这让我想到了另一个问题。我会/应该在工厂功能中添加什么?有没有一种方法可以让它变得通用呢?或者我需要100种不同的方法来表示100个不同的类,这些类可以放入我的集合中?@Jeroen:我不能说这是最漂亮的解决方案,但它很灵活。假设OP不想为其类型定义无参数构造函数,和/或他希望
Id
属性为只读。另一种方法是定义一个受保护的抽象
CreateEntity
方法,但这将强制OP从
CacheCollection
继承他想要的每个类型的集合(不是很吸引人)。我个人认为
new
约束在存在无参数构造函数的情况下很有用,但在需要添加一个构造函数才能满足约束的情况下就不那么有用了。@Sonny Boy:假设你
T entity = new T { Id = Id };
public class CacheCollection<T> : List<CacheItem<T>> where T : EntityBase, new()
public class CacheCollection<T> : List<CacheItem<T>> where T : EntityBase, new(), IIdSetter
var constructor = entitType.GetConstructor(new Type[] { typeof(int) });
return (EntityBase)constructor.Invoke(new object[] { id });
// add new() to your where clause:
public class CacheCollection<T> : List<CacheItem<T>> where T : EntityBase, new()

// change the creation of the object:
...
T entity = new T();
entity.Id = Id;
...
public interface IId
{
   public Id { get; set; }
}
public partial MyClass : IId { }
public class CacheCollection<T> : List<CacheItem<T>> where T : EntityBase, IId, new()
T entity = (T)Activator.CreateInstance(typeof(T), Id);