Универсальные параметры интерфейса в универсальном интерфейсе

1

У меня есть следующий общий интерфейс:

public interface IContainer<T>

и общий интерфейс, который выступает в качестве контейнера для этих общих интерфейсов:

public interface IGroupOfContainers<TContainer, TValue>
    where TContainer : IContainer<TValue>

Теперь я могу определить некоторые классы:

public class SomeContainer<T> : IContainer<T>
...

public class OtherContainer<T> : IContainer<T>
...

public class SomeGroupOfContainers<TContainer, TValue>
    : IGroupOfContainers<TContainer, TValue>
    where TContainer : IContainer<TValue>
...

public class OtherGroupOfContainers<TContainer, TValue>
    : IGroupOfContainers<TContainer, TValue>
    where TContainer : IContainer<TValue>
...

Теперь я хочу, чтобы IntGroupManager мог работать с любым объектом, который соответствует IGroupOfContainers<IContainer<int>, int> interface:

public class IntGroupManager
{
    public IGroupOfContainers<IContainer<int>, int> IntGroup { get; set; }
    ...
}

Но когда я пытаюсь:

var manager= new IntGroupManager();
manager.IntGroup = new GroupOfContainers<Container<int>, int>
    as IGroupOfContainers<IContainer<int>, int>;

Я получаю "Подозрительное приведение: нет типа, который унаследован от обоих GroupOfContainers<Container<int>, int> и IGroupOfContainers<IContainer<int>, int> ", и когда я пытаюсь запустить код, manager.IntGroup имеет значение null, поскольку сбой выполнен.

Если я изменил сигнатуру типа свойства IntGroup на

public IGroupOfContainers<Container<int>, int> IntGroup { get; set; }

(изменение IContainer в конкретный класс Container), код выполняется правильно. Тем не менее, я тогда не смог бы создать экземпляр IntGroupManager с помощью типа IntGroup, например GroupOfContainers<OtherContainer<int>, int>, поскольку OtherContainer не является производным от Container.

Я мог бы ввести общие параметры в определение IntGroupManager, возможно, переписывая его как

public class IntGroupManager<TContainer> where TContainer : IContainer<int>

Однако это привяжет любой экземпляр IntGroupManager к определенному типу IContainer; Я бы не смог

var manager = new IntGroupManager<Container<int>>();

// Set manager.IntGroup and do some things with it
// This is okay
manager.IntGroup = new GroupOfContainers<Container<int>, int>();
// manager executes some code involving IntGroup and manipulates its internal state

// Set manager.IntGroup to something else and do things with it
// This is not okay, since OtherContainer<int> is not derived from Container<int>
manager.IntGroup = new GroupOfContainers<OtherContainer<int>, int>();

и поэтому я теряю некоторую гибкость.

Как я могу правильно определить свойство IntGroup для любого типа, соответствующего IGroupOfContainers<IContainer<int>, int>? Я хочу иметь возможность использовать тот же экземпляр IntGroupManager с любым объектом IntGroup который соответствует IGroupOfContainers<IContainer<int>, int>. Есть ли способ сделать это?

Теги:
generics
interface

2 ответа

1
Лучший ответ

Хотя трудно сказать, не видя, как вы использовали TContainer и TValue (ввод или вывод?), Но попытались ли вы использовать ключевое слово out для аргументов общего типа? Для получения дополнительной информации вы можете проверить ковариацию и контравариантность

public interface IContainer<out T>

public interface IGroupOfContainers<out TContainer, TValue>
    where TContainer : IContainer<TValue>
  • 0
    Если бы это был либо ввод, либо вывод, а не оба, это было бы решением, верно? Есть ли способ сделать это, даже если IGroupOfContainers определил функции, которые использовали TContainer в качестве входа и выхода?
2

Это ответ на ваш вопрос в комментариях ответа Мехмета (извините, слишком долго для комментария):

Если бы это был вход или выход, а не оба, [ковариация] была бы решением, верно? Есть ли способ сделать это, даже если IGroupOfContainers определил функции, которые использовали TContainer как входные и выходные данные?

Продолжим ваш пример и предположим, что работает следующее утверждение:

var g = new GroupOfContainers<Container<int>, int>
    as IGroupOfContainers<IContainer<int>, int>;

Предположим, что IGroupOfContainers<TContainer, TValue> имеет метод AddContainer(TContainer):

g.AddContainer(new OtherContainer<int>());

Это будет скомпилировано, так как OtherContainer<int> является IContainer<int>.

Тем не менее, вы просто добавили OtherContainer в группу Container s! Вот почему вы не можете делать то, что хотите, если вы не запретите IGroupOfContainers принимать входы TContainer.

  • 0
    Это очень ясно объясняет, почему C # ведет себя так. Спасибо за разъяснения!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню