I am trying to create a generic class which new's up an instance of the generic type. As follows:
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;
}
}
But I get the following error:
cannot provide arguments when creating an instance of a variable type
I found the following related question which is very close to what I need: http://stackoverflow.com/questions/840261/c-generic-new-constructor-problem/1681770#1681770
However, I can't used Jared's suggested answer as I am calling the method within the Generic class, not outside of it, so I can't specify the concrete class.
Is there a way around this?
I have tried the following based on the other question, but it doesn't work as I don't know the concrete type of T to specify. As it is called from inside the generic class, not outside:
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;
}
}
****EDIT : ADDED POSSIBLE SOLUTIONS**
So I have tested 2 possible solutions:
First is exactly as explained below by Jon Skeet. This definitely works but means having an obscure lambda in the constructor. I am not very comfortable with this as it means users need to know the correct lambda that is expected. After all, they could pass a lambda which doesn't new up the type, but does something entirely unexpected
Secondly, I went down the Factory method route; I added a Create method to the common interface:
IJewellerHomepageCarouselItem Create(PageData pageData);
Then provided an implementation in each Concrete class:
public IJewellerHomepageCarouselItem Create(PageData pageData)
{
return new JewellerHomepageCarouselItem(pageData, null);
}
And used a two step initialisation syntax:
T carouselItem = new T();
T homepageMgmtCarouselItem = (T) carouselItem.Create(jewellerPage);
Would love to hear some feedback on the merit of each of these approaches.