Reputation: 1077
Suppose I have a parent class Animal
, with subclasses Cat
and Dog
. I also have a Building<T>
where T can be of any Animal
.
public class<T> Building
{
public T Inhabitant {get; set;}
}
Now, I want to create some buildings with cats and some with dogs (based on parameters available only at runtime). For this, I am trying to create following method:
private Building<Animal> populateBuilding(string animalKind)
{
if(animalKind == "cat")
{
return new Building<Cat>;
}
if(animalKind == "dog")
{
return new Building<Dog>;
}
return null;
}
However, I cannot do this because compiler tells that Building<Dog>/Building<Cat>
cannot be cast to Building<Animal>
. I found several examples where similar issue is described but with using of lists (e.g. List<Animal>, List<Dog>, List<Cat>
). However, I do not use lists in my case, and wonder if it is still same situation.
Upvotes: 0
Views: 67
Reputation: 2373
You should read some article on covariance and contravariance, for instance, this one.
In your case the class Building
is so designed that it does not allow for either.
Suppose it were allowed to use Building
covariantly. Then it would permit the following code snippet:
var dogHouse = new Building<Dog>();
var house = (Building<Animal>) dogHouse; // Casting to base class
house.Inhabitant = new Cat(); // Inhabitant of a Building<Animal> is any Animal
dogHouse.Inhabitant.Bark(); // Meow!
A generic may be used covariantly with its type parameter T
if its methods only return values of type T
(as return values or out
-parameters). A generic may be used covariantly with its type parameter T
if its methods only accept values of type T
as parameters. In your case, there are both, namely, get_Inhabitant
returns an Animal
and set_Inhabitant
accepts an Animal
.
Moreover, in C#, as it has been rightly pointed out in comments, the concepts of covariance and contravariance are only applied to interfaces and delegates.
Upvotes: 3
Reputation: 5119
You should be able to do it as follows:
private Building<Animal> populateBuilding(string animalKind)
{
var building = new Building<Animal>();
if (animalKind == "cat")
{
return new Building<Animal>
{
Inhabitant = new Cat()
};
}
if (animalKind == "dog")
{
return new Building<Animal>
{
Inhabitant = new Dog()
};
}
return null;
}
Upvotes: 0