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

Chr*_*sCa 20 c# generics

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

public class HomepageCarousel<T> : List<T>
    where T: IHomepageCarouselItem, new()
{
    private List<T> GetInitialCarouselData()
    {
        List<T> carouselItems = new List<T>();

        if (jewellerHomepages != null)
        {
            foreach (PageData pageData in jewellerHomepages)
            {
               T item = new T(pageData); // this line wont compile
               carouselItems.Add(item);
            }
        }
        return carouselItems;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是我收到以下错误:

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

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

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

有没有解决的办法?

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

public class HomepageCarousel<T> : List<T>
    where T: IHomepageCarouselItem, new()
{

    private List<T> 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<T> GetInitialCarouselData(Func<PageData, T> del)
    {
        List<T> carouselItems = new List<T>();

        if (jewellerHomepages != null)
        {
            foreach (PageData pageData in jewellerHomepages)
            {
                T item = del(pageData);
                carouselItems.Add(item);
            }
        }
        return carouselItems;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

IJewellerHomepageCarouselItem Create(PageData pageData);
Run Code Online (Sandbox Code Playgroud)

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

public IJewellerHomepageCarouselItem Create(PageData pageData)
{
     return new JewellerHomepageCarouselItem(pageData, null);
}
Run Code Online (Sandbox Code Playgroud)

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

T carouselItem = new T();
T homepageMgmtCarouselItem = (T) carouselItem.Create(jewellerPage);
Run Code Online (Sandbox Code Playgroud)

希望听到一些关于这些方法的优点的反馈.

Qui*_*son 20

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

T homepageMgmtCarouselItem = Activator.CreateInstance(typeof(T), pageData) as T;
Run Code Online (Sandbox Code Playgroud)


Jon*_*eet 18

Jared的答案仍然是一个很好的方法 - 你只需要让构造函数接受Func<PageData, T>并存储它以供日后使用:

public class HomepageCarousel<T> : List<T> where T: IHomepageCarouselItem
{
    private readonly Func<PageData, T> factory;

    public HomepageCarousel(Func<PageData, T> factory)
    {
        this.factory = factory;
    }

    private List<T> GetInitialCarouselData()
    {
       List<T> carouselItems = new List<T>();

       if (jewellerHomepages != null)
       {
            foreach (PageData pageData in jewellerHomepages)
            {
                T homepageMgmtCarouselItem = factory(pageData);
                carouselItems.Add(homepageMgmtCarouselItem);
            }
       }
       return carouselItems;
    }
Run Code Online (Sandbox Code Playgroud)

然后你只需将函数传递给你在其中创建新实例的构造函数HomepageCarousel<T>.

(我建议使用组合而不是继承,顺便说一句......从中推出List<T>几乎总是错误的方法.)

  • 这不是我想到的性能影响 - 它是"没有发现事情在执行时间之前就被打破"的方面. (3认同)

Gro*_*roo 5

只是为了添加其他答案:

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

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

// get the initial list
List<PageData> pageDataList = GetJewellerHomepages();

// project each item using a delegate
List<IHomepageCarouselItem> carouselList =
       pageDataList.Select(t => new ConcreteCarousel(t));
Run Code Online (Sandbox Code Playgroud)

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

public class Project
{
    public static IEnumerable<Tdest> From<Tsource, Tdest>
        (IEnumerable<Tsource> source, Func<Tsource, Tdest> projection)
    {
        foreach (Tsource item in source)
            yield return projection(item);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后使用它像:

// get the initial list
List<PageData> pageDataList = GetJewellerHomepages();

// project each item using a delegate
List<IHomepageCarouselItem> carouselList =
       Project.From(pageDataList, 
           delegate (PageData t) { return new ConcreteCarousel(t); });
Run Code Online (Sandbox Code Playgroud)

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

[编辑]想想以下内容:

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

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

    // ...
}
Run Code Online (Sandbox Code Playgroud)

我认为是这种情况,因为你正在访问jewellerHomepages方法中的一个字段(所以我猜你将它存储在ctor中).

这种方法存在一些问题.

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

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

    public class HomepageCarousel(List<PageData> jewellerHomepages)
    {
        // do not save the reference to jewellerHomepages
        this.AddRange(GetInitialCarouselData(jewellerHomepages));
    }
    
    Run Code Online (Sandbox Code Playgroud)

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

    HomepageCarousel<ConcreteCarousel> list =
         new HomepageCarousel<ConcreteCarousel>(listOfPageData);
    
    Run Code Online (Sandbox Code Playgroud)

    你有没有选择用其他任何东西来实例化它?即使添加了新的构造GetInitialCarouselData函数,您的方法仍然太具体,不能仅用PageData作源.

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