Reputation: 19397
Let's say I have a base class named Animal
and two derived classes named Lion
and Tiger
. And I'd like to also have a generic type named AnimalList<Animal>
to contain members of Animal
. For example:
var list = new AnimalList<Animal>();
list.Add(new Lion());
list.Add(new Tiger());
Very straightforward so far but there's a catch...I'd like the base class Animal
to be abstract
. That is, I don't want to allow an instance of the base class to be created. However, making the base class abstract not surprisingly results in error CS0310 (see a complete example below).
I did come up with a solution: Make the base class non-abstract as required and simply throw an exception from the default (parameterless) constructor:
public Animal()
{
throw new System.NotImplementedException();
}
But this makes me feel a little uneasy. Is there a better way?
Here's a complete example that illustrates the scenario that I describe above:
// public class Animal {} // OK
public abstract class Animal {} // 'abstract' keyword causes error 'CS0310'
public class Lion : Animal {}
public class Tiger : Animal {}
public class AnimalList<T> :
Animal
where T : Animal, new()
{
public void Add(T animal) {}
}
class AnimalListDemo
{
static public void Main(string[] args)
{
// The following statement causes:
// error CS0310 - 'Animal' must be a non-abstract type with a
// public parameterless constructor in order to use it as
// parameter 'T' in the generic type or method 'AnimalList<T>'
var list = new AnimalList<Animal>();
list.Add(new Lion());
list.Add(new Tiger());
}
}
Upvotes: 1
Views: 5868
Reputation: 19397
At the end of my question, I asked:
Is there a better way?
My conclusion from the helpful answers on this page: Yes!
In particular, in his comment to my question, @CodesInChaos said the following:
In general I'd consider the
new()
constraint a bit of a code smell.
This got me searching for an alternative to the new()
constraint. (I do indeed have the need to create instances of the generic type parameter in a base class within my class hierarchy.)
I found what I was looking for in this answer to Create a new instance of T without the new constraint:
var instanceOfT = new T(); // Before
var instanceOfT = (T)Activator.CreateInstance(typeof(T)); // After
This allowed me to drop the few new()
constraints that I had in my source code. And finally, dropping the new()
constraints allowed me to set an intermediate class to abstract
as I desired.
Upvotes: 2
Reputation: 7724
Interfaces are allowed to be combined with the new
constraint:
public interface IAnimal {}
public abstract class Animal : IAnimal {}
public class AnimalList<T> where T : IAnimal, new() {}
However, your first line will still fail:
var list = new AnimalList<Animal>();
AnimalList<Animal>
(or AnimalList<IAnimal>
) is not a valid type because the given T
can't be constructed. Are you sure new is what you want?
Upvotes: 0
Reputation: 7813
Is it absolutely necessary to have the new()
constraint on the generic parameter? If you remove it, it will compile, at the cost of not allowing creating new instances of T
within AnimalList
.
Reason for that is that if you request the class to have a public, parameterless constructor, it cannot be abstract, because abstract classes are meant specifically to be non-creatable, but you're requesting the class to be creatable with the new()
. As those requirements contradict each other, the compiler complains.
The solution seems to be rather a hack, and will not work either, because the derived class constructor will also call the base class constructor (instead of completely override it) and still throw the exception.
Upvotes: 2
Reputation: 44439
Just remove the new()
constraint. It requires you to provide a class that can be instantiated through new()
(which is what the requirement is all about), but an abstract class obviously can't do this.
From MSDN:
The new constraint specifies that any type argument in a generic class declaration must have a public parameterless constructor. To use the new constraint, the type cannot be abstract.
Upvotes: 4