Hector Llanos
Hector Llanos

Reputation: 317

C# casting derived generic type to parent

Before I ask my question this is my structure:

public class Data : ScriptableObject {...}
public class ItemData : Data {...}
public class WeaponData : ItemData {...}

public abstract class Item<T> : Visual<T> where T : ItemData {...}
public class Weapon<T> : Item<T> where T : WeaponData {...}

I get an error (Cannot cast from source type to destination type) when I create a Weapon object and assign it to Item<ItemData>.

Weapon<Foo> weapon = new Weapon<Foo>();
Item<ItemData> other = weapon;

Why is that?

Upvotes: 4

Views: 3097

Answers (2)

David L
David L

Reputation: 33815

In C#, covariance (assigning a derived type to a base type) cannot be applied to generic classes. As a result, you would need to apply an interface specifically marked as covariant, using the out parameter modifier on a new IItem interface.

However, that by itself isn't enough. You do not have a way to tell Item that it will allow an instance of Weapon to be assigned to it when Weapon has a generic type parameter that could be anything provided it inherits from ItemData. This places the compiler in a difficult predicament since it can't assure you that the relationship is valid. However, if you apply a new IItemData interface to ItemData, the cast will be permissible.

void Main()
{
    Weapon<Foo> weapon = new Weapon<Foo>();
    IItem<ItemData> other = weapon;
}

public interface IItem<out T> {}
public interface IItemData {}

public class Foo : WeaponData {}

public class Data : ScriptableObject {}
public class ItemData : Data, IItemData {}
public class WeaponData : ItemData {}

public abstract class Item<T> : Visual<T>, IItem<T> where T : IItemData {}
public class Weapon<T> : Item<T> where T : WeaponData {}

public class ScriptableObject {}
public class Visual<T> {}

This requires Item<T> be updated to be constrained to IItemData instead of constrained to a concrete type.

Upvotes: 8

MutantOctopus
MutantOctopus

Reputation: 3581

Generics get all weird when you deal with this kind of stuff. I've not investigated this in C#, but if it's anything like Java - which I suspect it is - you can only set a variable of a given generic type if the generic parameters match. For example, other would have to be an Item of type Foo (Item<Foo>), not of type ItemData. The reason goes something like this:

class A {...}
class B : A {...}
class C {
    public static void main(string[] args) {
        List<B> BL = new List<B>();
        List<A> AL = BL;
        AL.Add(new A()); // Now the List of B instances has an A instance in it!!
    }
}

I'm not sure if there's a way to do something like Java's List<? extends A> in C#, though. That would be the Java-type solution.

Upvotes: 0

Related Questions