Немного путаницы с C # Generics, T, Abstraction? - PullRequest
0 голосов
/ 23 сентября 2018

Я - создатель игр на C # с Unity.У меня есть система управления коллекцией.

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

Коллекционирование

public abstract class Collectable : CollectableRelatedMonoBehaviour, IHasPlayableSound

Collectable_Money

public class Collectable_Money : Collectable

CollectableParent_Money

public class CollectableParent_Money : SpawnPointParentDefinedCollectableParent<Collectable_Money>

ПРОБЛЕМА

"collactableParentsList" в CollectableManager не принимает SpawnPointParentDefinedCollectableParent<Collectable_Money> как элемент, когда Tопределяется как "Collectable_Money", он является производным от "Collectable".Если я сделаю это SpawnPointParentDefinedCollectableParent<Collectable>, он будет принят как элемент в список.

1 Ответ

0 голосов
/ 23 сентября 2018

Проблема здесь в том, что, хотя Collectable_Money является подклассом Collectable, это не делает SpawnPointParentDefinedCollectableParent<Collectable_Money> подклассом CollectableParent<Collectable>.

Возможно, это можно исправить с помощью универсальных интерфейсов.Если мы изменим CollectableParent<T> на интерфейс и определим его как ковариант в T через модификатор out.(Вы не предоставили определение для CollectableRelatedMonoBehaviour, но его также необходимо преобразовать в интерфейс):

public interface ICollectableParent<out T> : ICollectableRelatedMonoBehaviour where T : Collectable

Если затем определить список как:

public static List<ICollectableParent<Collectable>> collectableParentsList;

Затем вы можете успешно добавить к нему элементы типа SpawnPointParentDefinedCollectableParent<Collectable_Money>.

Критическая часть здесь заключается в том, что интерфейс ICollectableParent<T> ковариантен в T.Это позволяет передать экземпляр, который реализует ICollectableParent<Collectable_Money>, где ожидается ICollectableParent<Collectable>.

Определение интерфейса как ковариантного в T действительно вводит некоторые ограничения, конкретно (и как подразумевается в * 1031)* модификатор * используется для указания ковариации), тип T должен использоваться только как возвращаемое значение в методах интерфейса.Например:

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);
}

Подробнее о ковариации и ее обратной (контравариантности) вы можете прочитать здесь: Разница между ковариацией и контрастностью

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...