Search code examples
c#genericsdowncast

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?


Solution

  • 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.