C#generics问题 – 使用构造函数中的参数新建generics类型

我正在尝试创建一个generics类,其中new是generics类型的实例。 如下:

public class HomepageCarousel : List where T: IHomepageCarouselItem, new() { private List GetInitialCarouselData() { List carouselItems = new List(); if (jewellerHomepages != null) { foreach (PageData pageData in jewellerHomepages) { T item = new T(pageData); // this line wont compile carouselItems.Add(item); } } return carouselItems; } } 

但是我收到以下错误:

在创建变量类型的实例时无法提供参数

我发现了以下相关的问题,它与我需要的非常接近:将参数传递给模板化类型的C#generic new()

但是,我不能使用Jared建议的答案,因为我在Generic类中调用方法,而不是在它之外,所以我不能指定具体的类。

有没有解决的办法?

我已根据另一个问题尝试了以下内容,但它不起作用,因为我不知道要指定的具体类型。 从generics类中调用它,而不是在外面调用:

 public class HomepageCarousel : List where T: IHomepageCarouselItem, new() { private List LoadCarouselItems() { if (IsCarouselConfigued) { return GetConfiguredCarouselData(); } // ****** I don't know the concrete class for the following line, // so how can it be instansiated correctly? return GetInitialCarouselData(l => new T(l)); } private List GetInitialCarouselData(Func del) { List carouselItems = new List(); if (jewellerHomepages != null) { foreach (PageData pageData in jewellerHomepages) { T item = del(pageData); carouselItems.Add(item); } } return carouselItems; } } 

********编辑:增加可能的解决方案**

所以我测试了两种可能的解决方案:

首先,正如Jon Skeet所述 。 这绝对有效,但意味着在构造函数中有一个模糊的lambda。 我对此不太满意,因为这意味着用户需要知道正确的lambda。 毕竟,他们可以通过一个不是新类型的lambda,但做了一些完全出乎意料的事情

其次,我走了工厂方法路线; 我在公共接口中添加了一个Create方法:

 IJewellerHomepageCarouselItem Create(PageData pageData); 

然后在每个Concrete类中提供了一个实现:

 public IJewellerHomepageCarouselItem Create(PageData pageData) { return new JewellerHomepageCarouselItem(pageData, null); } 

并使用了两步初始化语法:

 T carouselItem = new T(); T homepageMgmtCarouselItem = (T) carouselItem.Create(jewellerPage); 

很想听听关于这些方法的优点的一些反馈。

Jared的答案仍然是一个很好的方法 – 你只需要让构造函数获取Func并将其存储起来以供日后使用:

 public class HomepageCarousel : List where T: IHomepageCarouselItem { private readonly Func factory; public HomepageCarousel(Func factory) { this.factory = factory; } private List GetInitialCarouselData() { List carouselItems = new List(); if (jewellerHomepages != null) { foreach (PageData pageData in jewellerHomepages) { T homepageMgmtCarouselItem = factory(pageData); carouselItems.Add(homepageMgmtCarouselItem); } } return carouselItems; } 

然后,您只需将该函数传递给构造函数,您可以在其中创建HomepageCarousel的新实例。

(我建议使用组合而不是inheritance,顺便说一句……从List派生出来几乎总是错误的方法。)

您是否考虑过使用Activator(这只是另一种选择)。

 T homepageMgmtCarouselItem = Activator.CreateInstance(typeof(T), pageData) as T; 

只是为了添加其他答案:

你在这里做的基本上叫做投影 。 您有一个类型的List ,并希望将每个项目(使用委托)投影到不同的项目类型。

因此,一般操作序列实际上是(使用LINQ):

 // get the initial list List pageDataList = GetJewellerHomepages(); // project each item using a delegate List carouselList = pageDataList.Select(t => new ConcreteCarousel(t)); 

或者,如果您使用.Net 2.0,您可以编写一个帮助程序类,如:

 public class Project { public static IEnumerable From (IEnumerable source, Func projection) { foreach (Tsource item in source) yield return projection(item); } } 

然后使用它像:

 // get the initial list List pageDataList = GetJewellerHomepages(); // project each item using a delegate List carouselList = Project.From(pageDataList, delegate (PageData t) { return new ConcreteCarousel(t); }); 

我不确定代码的其余部分是什么样的,但我相信GetInitialCarouselData不是处理初始化的正确位置,特别是因为它基本上复制了投影function(这是非常通用的,可以在单独的类中提取,像Project )。

[编辑]想想以下内容:

我相信你的类现在有这样的构造函数:

 public class HomepageCarousel : List where T: IHomepageCarouselItem, new() { private readonly List jewellerHomepages; public class HomepageCarousel(List jewellerHomepages) { this.jewellerHomepages = jewellerHomepages; this.AddRange(GetInitialCarouselData()); } // ... } 

我认为是这种情况,因为您正在访问方法中的jewellerHomepages字段(因此我猜您将它存储在ctor中)。

这种方法存在一些问题。

  • 您可以参考jewellerHomepages ,这是不必要的。 您的列表是IHomepageCarouselItems的列表,因此用户可以简单地调用Clear()方法并用他们喜欢的任何东西填充它。 然后你最终得到了一些你没有使用的东西。

  • 你可以通过简单地删除字段来解决这个问题:

     public class HomepageCarousel(List jewellerHomepages) { // do not save the reference to jewellerHomepages this.AddRange(GetInitialCarouselData(jewellerHomepages)); } 

    但是如果你意识到你可能想要使用与PageData不同的其他类来初始化它会发生什么? 现在,您正在创建如下列表:

     HomepageCarousel list = new HomepageCarousel(listOfPageData); 

    你有没有选择用其他任何东西来实例化它? 即使添加了新的构造GetInitialCarouselDataGetInitialCarouselData方法仍然过于具体,不能仅使用PageData作为源。

结论是:如果不需要,请不要在构造函数中使用特定类型。 在其他地方创建实际列表项(具体实例)。

这是C#和CLR的障碍,你不能将参数传递给新的T(),简单。

如果你是来自C ++背景,那么这个使用是非破坏和TRIVIAL。 加上你甚至不需要接口/约束。 破坏了整个地方,没有那个function性工厂3.0黑客,你被迫做2次通过初始化。 管理亵渎!

首先执行新的T()然后设置属性或传递一个奇特的初始化语法,或者所有建议都使用Pony的function性解决方法..所有令人讨厌但这是“generics”的编译器和运行时概念。

还有另一种可能的解决方案,相当脏。

使IHomepageCarouselItem具有“Construct”方法,该方法将pageData作为参数并返回IHomepageCarouselItem。

然后这样做:

  T factoryDummy = new T(); List carouselItems = new List(); if (jewellerHomepages != null) { foreach (PageData pageData in jewellerHomepages) { T homepageMgmtCarouselItem = (T)factoryDummy.Construct(pageData); carouselItems.Add(homepageMgmtCarouselItem); } } return carouselItems; 

我可能会去寻找Tony“jon”Skeet小马的建议,但还有另一种方法。 所以主要是为了这里的乐趣,这是一个不同的解决方案(如果您忘记实现所需的方法,但在运行时失败,但是不必提供工厂方法的好处,编译器会神奇地将您连接起来。

 public class HomepageCarousel : List where T: IHomepageCarouselItem { private List GetInitialCarouselData() { List carouselItems = new List(); if (jewellerHomepages != null) { foreach (PageData pageData in jewellerHomepages) { T homepageMgmtCarouselItem = null; homepageMgmtCarouselItem = homepageMgmtCarouselItem.create(pageData); carouselItems.Add(homepageMgmtCarouselItem); } } return carouselItems; } } public static class Factory { someT create(this someT, PageData pageData) { //implement one for each needed type } object create(this IHomepageCarouselItem obj, PageData pageData) { //needed to silence the compiler throw new NotImplementedException(); } } 

只是为了重复我的“免责声明”,这是一个非常重要的提醒,可以有相当不同的方法来解决同样的问题,他们都有退缩和优势。 这种方法的一个缺点是它是黑魔法的一部分;)

 T homepageMgmtCarouselItem = null; homepageMgmtCarouselItem = homepageMgmtCarouselItem.create(pageData); 

但是你要避免使用委托参数的特殊构造函数。 (但我通常会采用这种方法,除非我使用dependency injection机制为我提供工厂类。我在业余时间正在研究哪种DI框架; p)

你为什么不在接口上放一个静态的“构造函数”方法? 我知道有点hacky,但你必须做你要做的事……