Reputation: 317
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
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
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