I am a C# game maker with Unity. I have collectable management system.
CollectableManager
public List<CollectableParent<Collectable>> collactableParentsList;
CollectableParent
public class CollectableParent<T> : CollectableRelatedMonoBehaviour where T : Collectable
SpawnPointDefinedCollectableParent
public class SpawnPointParentDefinedCollectableParent<T> : CollectableParent<T> where T : Collectable
Collectable
public abstract class Collectable : CollectableRelatedMonoBehaviour, IHasPlayableSound
Collectable_Money
public class Collectable_Money : Collectable
CollectableParent_Money
public class CollectableParent_Money : SpawnPointParentDefinedCollectableParent<Collectable_Money>
PROBLEM
"collactableParentsList" in CollectableManager does not accept SpawnPointParentDefinedCollectableParent<Collectable_Money>
as item when T is defined as "Collectable_Money", it is derived from "Collectable". If I do this SpawnPointParentDefinedCollectableParent<Collectable>
, it is accepted as item into list.
The problem here is that even though Collectable_Money
is a sub-class of Collectable
, that does not make SpawnPointParentDefinedCollectableParent<Collectable_Money>
a sub-class of CollectableParent<Collectable>
.
It may be possible to fix this by using generic interfaces. If we change CollectableParent<T>
to be an interface, and define it to be covariant in T
via the out
modifier. (You haven't provided the definition for CollectableRelatedMonoBehaviour
, but it will also need to be converted to an interface):
public interface ICollectableParent<out T> : ICollectableRelatedMonoBehaviour where T : Collectable
If you then define the list as:
public static List<ICollectableParent<Collectable>> collectableParentsList;
Then you can successfully add items of type SpawnPointParentDefinedCollectableParent<Collectable_Money>
to it.
The critical part here is that the ICollectableParent<T>
interface is covariant in T
. This makes it possible to pass an instance that implements ICollectableParent<Collectable_Money>
where an ICollectableParent<Collectable>
is expected.
Defining the interface as covariant in T
does introduce some restrictions, specifically (and as implied by the out
modifier used to indicate covariance), the type T
must only be used as a return value on the interface's methods. For example:
public interface ICollectableParent<out T> : ICollectableRelatedMonoBehaviour where T : Collectable
{
// This is allowed - T is used as a return type
T GetChild();
// This is *not* allowed - T is used as a parameter
void SetChild(T child);
}
You can read more about covariance, and its inverse (contravariance) here: Difference between Covariance & Contra-variance