-1

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.

LazyX
  • 177
  • 2
  • 10

1 Answers1

1

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

Iridium
  • 23,323
  • 6
  • 52
  • 74
  • Thank you for great answer, the limitation of void SetChild(T child) really effected me, I propably rethink whole design again, it looks like generics limits me in this case. – LazyX Sep 24 '18 at 07:01