У меня есть следующий общий интерфейс:
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>
. Есть ли способ сделать это?
Хотя трудно сказать, не видя, как вы использовали TContainer
и TValue
(ввод или вывод?), Но попытались ли вы использовать ключевое слово out
для аргументов общего типа? Для получения дополнительной информации вы можете проверить ковариацию и контравариантность
public interface IContainer<out T>
public interface IGroupOfContainers<out TContainer, TValue>
where TContainer : IContainer<TValue>
Это ответ на ваш вопрос в комментариях ответа Мехмета (извините, слишком долго для комментария):
Если бы это был вход или выход, а не оба, [ковариация] была бы решением, верно? Есть ли способ сделать это, даже если 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
.