Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# generics problem - newing up the generic type with parameters in the constructor

Tags:

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: Passing arguments to C# generic new() of templated type

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.

like image 460
ChrisCa Avatar asked Nov 05 '09 17:11

ChrisCa


People also ask

Bahasa C digunakan untuk apa?

Meskipun C dibuat untuk memprogram sistem dan jaringan komputer namun bahasa ini juga sering digunakan dalam mengembangkan software aplikasi. C juga banyak dipakai oleh berbagai jenis platform sistem operasi dan arsitektur komputer, bahkan terdapat beberepa compiler yang sangat populer telah tersedia.

C dalam Latin berapa?

C adalah huruf ketiga dalam alfabet Latin. Dalam bahasa Indonesia, huruf ini disebut ce (dibaca [tʃe]).

Bahasa C dibuat pertama kali oleh siapa dan tahun berapa?

Bahasa pemrograman C ini dikembangkan antara tahun 1969 – 1972 oleh Dennis Ritchie. Yang kemudian dipakai untuk menulis ulang sistem operasi UNIX. Selain untuk mengembangkan UNIX, bahasa C juga dirilis sebagai bahasa pemrograman umum.


2 Answers

Have you considered using Activator (this is just another option).

T homepageMgmtCarouselItem = Activator.CreateInstance(typeof(T), pageData) as T;
like image 136
Quintin Robinson Avatar answered Sep 17 '22 16:09

Quintin Robinson


Jared's answer is still a good way to go - you just need to make the constructor take the Func<PageData, T> and stash it for later:

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

Then you just pass the function into the constructor where you create the new instance of the HomepageCarousel<T>.

(I'd recommend composition instead of inheritance, btw... deriving from List<T> is almost always the wrong way to go.)

like image 30
Jon Skeet Avatar answered Sep 20 '22 16:09

Jon Skeet