Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to force downcast on generics

Given the code below:

class Animal
{ }

class Dog : Animal
{ }

class Cage<T>
{
    private T animal;

    public Cage(T animal)
    {
        this.animal = animal;
    }

    public T Animal
    { 
        get { return animal;} 
    }
}

class Program
{
    static void Main(string[] args)
    {
        Dog dog = new Dog();
        Cage<Animal> animalCage = new Cage<Animal>(dog);
        Cage<Dog> dogCage = (Cage<Dog>)animalCage;
    }
}

How can I workaround the last compiler error (conversion from animalCage to dogCage)?

In my code I know that the cage contains a dog, but I'm not able to find a way to cast to it. Is my unique alternative to create a converter and create a new Cage<Dog> instance from the value of a Cage<Animal>?

like image 410
user1778378 Avatar asked Oct 26 '12 22:10

user1778378


1 Answers

Problem #1: You cannot turn a Cage<Animal> instance into a Cage<Dog> instance, you need a Cage<Dog> instance (or an instance of a more specific type) whose reference is stored in a variable of a less specific type.

Change

Cage<Animal> animalCage = new Cage<Animal>(dog);
Cage<Dog> dogCage = (Cage<Dog>)animalCage;

to

Cage<Animal> animalCage = new Cage<Dog>(dog);
Cage<Dog> dogCage = (Cage<Dog>)animalCage;

Problem #2: You cannot store the reference to a Cage<Dog> instance in a Cage<Animal> variable, because classes do not support co-/contravariance.

Change

class Cage<T>
{
    ...

to

interface ICage<out T> 
{
    T Animal { get; }
}

class Cage<T> : ICage<T> 
{

and

Cage<Animal> animalCage = new Cage<Dog>(dog);
Cage<Dog> dogCage = (Cage<Dog>)animalCage;

to

ICage<Animal> animalCage = new Cage<Dog>(dog);
ICage<Dog> dogCage = (Cage<Dog>)animalCage;

Then it works. (If you do not change new Cage<Animal> to new Cage<Dog>, you get a cast exception at runtime.)

like image 107
dtb Avatar answered Oct 11 '22 02:10

dtb