简体   繁体   中英

How to fix Cast errors when casting down generic parameters

In some situations I need to cast down an object to an interface to fit my needs, which implicitly requires to cast down type arguments of generic interfaces.

Example

ICage<TAnimal> is the interface for a Cage of an animal of type IAnimal

public interface ICage<TAnimal>
    where TAnimal : IAnimal<IOwner>

public class Cage<TAnimal> : ICage<TAnimal>
    where TAnimal : IAnimal<IOwner>

public interface IAnimal<out TOwner>
    where TOwner : IOwner

IAnimal needs an Owner of type IOwner

public abstract class Mammal<TOwner> : IAnimal<TOwner>
    where TOwner : IOwner

A Mammal is a type of Animal with an Owner of type IOwner .

public class Human : IOwner

A Human is a type of IOwner

public class Dog<TOwner> : Mammal<TOwner>
    where TOwner : IOwner

A Dog is a type of Mammal.

Now putting everything together:

 var cage = new Cage<Mammal<IOwner>>();
 var me = new Human()
 {
     Name = "Hakim"
 };
 var dog = new Dog<Human>();
 dog.Owner = me;
 cage.Add((Mammal<IOwner>)dog);

In the last line I get a compile time error CS0030 telling me that I can not convert Dog to Mammel.

This is the same reason why you can't cast a List<string> to a List<object> .

Let's say in Mammal there is a property called Owner like this:

public TOwner Owner { get; set; }

For an instance of Mammal<IOwner> , this becomes:

public IOwner Owner { get; set; }

dog is a Dog<Human> , which is also a Mammal<Human> . If you could cast dog to Mammal<IOwner> , this would mean that dog.Owner can suddenly store any type that implements IOwner . ie this would be possible:

class EvilOwner : IOwner { ... }

Mammal<IOwner> mammal = (Mammal<IOwner>)dog;
mammal.Owner = new EvilOwner();

But that's not possible because dog at runtime is a Dog<Human> , which has an Owner of Human . EvilOwner cannot possibly be stored in Human !

What I suggest you do is to remove the TOwner generic parameter. If in your mind that Dog<Human> is also a kind of Mammal<IOwner> , then it probably makes more sense to design the classes like this:

public interface IAnimal {
    IOwner Owner { get; }
}

public abstract class Mammal : IAnimal { ... }

public class Dog : Mammal { ... }

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