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

时间:2009-11-05 17:41:15

标签: 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;
    }
}

但是我收到以下错误:

  

在创建变量实例时无法提供参数   输入

我发现以下相关问题非常接近我所需要的: Passing arguments to C# generic new() of templated type

但是,我不能像我一样使用贾瑞德建议的答案 在Generic类中调用该方法,而不是在 它,所以我不能指定具体的类。

有解决方法吗?

我已根据其他问题尝试了以下内容,但是 它不起作用,因为我不知道T的具体类型 指定。因为它是从泛型类中调用的,而不是 外:

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

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

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

首先,正如下面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);

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

7 个答案:

答案 0 :(得分:19)

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

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

答案 1 :(得分: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;
    }

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

(我建议使用构图而不是继承,顺便说一句......从List<T>派生出来几乎总是错误的方法。)

答案 2 :(得分: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));

或者,如果您使用的是.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);
    }
}

然后像:

一样使用它
// 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); });

我不确定代码的其余部分是什么样的,但我相信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());
    }

    // ...
}

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

这种方法存在一些问题。

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

  • 您只需删除字段即可解决此问题:

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

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

    HomepageCarousel<ConcreteCarousel> list =
         new HomepageCarousel<ConcreteCarousel>(listOfPageData);
    

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

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

答案 3 :(得分:1)

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

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

首先执行新的T(),然后设置属性或传递异国情调的初始化语法,或者所有建议都使用Pony的功能解决方法。所有这些都令人讨厌,但这就是“泛型”的编译器和运行时概念。< / p>

答案 4 :(得分:0)

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

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

然后这样做:

   T factoryDummy = new T();
   List<T> carouselItems = new List<T>();

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

答案 5 :(得分:0)

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

public class HomepageCarousel<T> : List<T> where T: IHomepageCarouselItem
{

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

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

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

答案 6 :(得分:0)

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