简体   繁体   中英

Why can't I cast an object to a constrained generic?

Given that I have the following interface and a class that implements it:

public interface IAnimal
{
    void Feed();
}

public class Animal : IAnimal
{
    public void Feed()
    {
        // feed
    }
}

Why is it not possible to cast from my concrete class to a generic constrained to be IAnimal, when my Animal class implements it?

public class Zoo
{
    private readonly Animal animal = new Animal();

    public TAnimal GetAnimal<TAnimal>() where TAnimal : IAnimal
    {
        return (TAnimal)(this.animal); // This will not compile
    }
}

If I change the field type of animal to be IAnimal rather than Animal then it compiles ok:

public class Zoo
{
    private readonly IAnimal animal = new Animal();

    public TAnimal GetAnimal<TAnimal>() where TAnimal : IAnimal
    {
        return (TAnimal)(this.animal); // this compiles
    }
}

Or alternatively if I do an explicit cast to IAnimal before the cast to the generic TAnimal then this will also compile fine:

public class Zoo
{
    private readonly Animal animal = new Animal();

    public TAnimal GetAnimal<TAnimal>() where TAnimal : IAnimal
    {
        return (TAnimal)((IAnimal)this.animal); // this compiles
    }
}

So my question is - why doesn't the first version work? Surely the compiler has enough information to know that Animal implements IAnimal, so given the constraint it would be valid to cast it to TAnimal? Or is there something I am missing here?

Animal inherits from IAnimal and TAnimal doesn't inherit from Animal , but from IAnimal too. You can't cast from A to B if B doesn't inherit from A , it would be something like:

DataTable dt = new DataTable();
string str = (string)dt;

You can't do this even though DataTable and string inherit from object . To convert between types that aren't related by inheritance you have to define conversion operators

Suppose you have also a class Dog :

public class Dog : IAnimal 
{

} 

If you invoke your method: GetAnimal<TAnimal>() with Dog as TAnimal you will have a code like this:

public class Zoo
{
    private readonly Animal animal = new Animal();

    public Dog GetAnimal()
    {
        return (Dog)this.animal;
    }
}

Of course Dog does not inherit from Animal so the casting is wrong.

Then you have your second example:

public class Zoo
{
    private readonly IAnimal animal = new Animal();

    public Dog GetAnimal()
    {
        return (Dog)this.animal;
    }
}

Dog is implementing IAnimal , so this will compile. It will however throw a InvalidCastException in runtime. The same thing applies to the double casting in your third example:

public class Zoo
{
    private readonly Animal animal = new Animal();

    public Dog GetAnimal()
    {
        return (Dog)(IAnimal)this.animal;
    }
}

You can cast from Animal to IAnimal and from IAnimal to Dog , however it will throw InvalidCastException .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM