4

As the title indicates, I would like to do the following, but the compiler won't let me:

public class MyClass
{
    private List<SomeSupertype> myList;

    public MyClass<T> (ICollection<T> elements) where T : SomeSupertype
    {
        myList = new List<SomeSuperType> ();
        foreach (SomeSupertype element in elements) {
            myList.Add (element);
        }
    }
}

Somehow, it does not seem to be possible to add a generic parameter to a constructor of a class which is itself not generic. Note that MyClass should be a non-generic type because a client should not have to distinguish between different types of MyClass once it is constructed.

Can anyone help me figure this out? Is this possible at all? It certainly is in Java :)

Edit: I forgot to mention that I am using .NET 3.5 and thus there is no support for covariant generics.

3
  • 5
    If you're using .NET 4, why not just have a non-generic constructor taking IEnumerable<SomeSupertype>? IEnumerable1` is covariant, so IEnumerable<T> is convertible to IEnumerable<SomeSupertype> whenever T : SomeSupertype. Commented Oct 29, 2011 at 8:35
  • @Anton: With some rewording and explanation, that seems answer-worthy Commented Oct 29, 2011 at 8:41
  • Yes, covariance would solve the problem. I probably should mention that I am using .NET 3.5 Commented Nov 1, 2011 at 8:18

3 Answers 3

7

Constructors in C# can't be generic (ditto events, properties, destructors, operators). Consider creating a static generic factory method instead:

public class MyClass
{
    private List<SomeSupertype> myList;

    private MyClass(List<SomeSupertype> myList)
    {
        this.myList = myList;
    }

    public static MyClass Create<T>(ICollection<T> elements)
        where T : SomeSupertype
    {
        var myList = new List<SomeSuperType>(elements);
        foreach (SomeSupertype element in elements)
        {
            myList.Add(element);
        }
        return new MyClass(myList);
    }
}

Even in .NET 3.5 you could use LINQ to make the list creation simpler, by the way:

var myList = elements.Cast<SomeSuperType>().ToList();
Sign up to request clarification or add additional context in comments.

Comments

6

You could use the factory pattern to solve your problem:

public class MyClass
{
    private List<SomeSuperType> myList = new List<SomeSuperType>();

    public static MyClass MyClassBuilder<T>(ICollection<T> elements) where T : SomeSuperType
    {
        var myClass = new MyClass();

        foreach (SomeSuperType element in elements)
        {
            myClass.myList.Add(element);
        }

        return myClass;
    }

    protected MyClass()
    {
    }
}

Or you could create two classes, like this:

// Put all the methods here
public class MyClass
{
    protected List<SomeSuperType> myList = new List<SomeSuperType>();

    protected MyClass()
    {
    }
}

// This class will define only the generic constructor
public class MyClass<T> : MyClass where T : SomeSuperType
{
    public MyClass(ICollection<T> elements)
    {
        foreach (SomeSuperType element in elements)
        {
            myList.Add(element);
        }
    }
}

(this ignoring the fact that, as written by Anton, in C# >= 4.0 you could simply accept an IEnumerable<SomeSuperType> and be happy)

Comments

0

You can't do this in C#. This is a good thing as we don't want constructors that don't match class. But still there are several ways you can accomplish your goal.

Easiest way (if you use C# 4), is to use IEnumerable instead of ICollection, because IEnumerable supports contravariance.

public class MyClass
{
    private List<SomeSupertype> myList;

    public MyClass (IEnumerable<SomeSuperType> elements)
    {
        myList = new List<SomeSuperType> ();
        foreach (SomeSupertype element in elements) {
            myList.Add (element);
        }
    }
}

Now you could call constructor without thinking about casting collection of derived types to collection of base types.

List<SomeDerivedClass> col = new List<SomeDerivedType>();
MyClass obj1 = new MyClass(col)

Second solution, you could just call your constructor by casting collection using LINQ.

MyClass obj1 = new MyClass(col.Cast<SomeSuperClass>());

Third solution, you could create static generic method for constructing new objects of your class.

public static MyClass Construct<T>(ICollection<T> elements) where T : ClassA
{
    return new MyClass(elements.Cast<ClassA>().ToArray());
}

Forth solution, you could make a generic class which derives from your base class as someone already pointed out.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.