Не получается закрывать дженерик наследником класса с другим закрытым дженериком в .net core
Допустим есть следующая иерархия:
public class ExampleHierarchy
{
public interface IValue
{}
public abstract class ValueClassBase : IValue
{}
public class ValueClass : ValueClassBase
{}
public interface IInterface1<TValue>
where TValue : IValue
{
TValue SomeValue { get; set; }
}
public abstract class RealizeInterface1Base<TValue> : IInterface1<TValue>
where TValue : IValue
{
public TValue SomeValue { get; set; }
}
public class RealizeInterface1 : RealizeInterface1Base<ValueClass>
{
}
public interface IInterface2<TTool>
where TTool : IInterface1<IValue>
{
TTool SomeTool { get; set; }
}
public abstract class RealizeInterface2Base<TTool> : IInterface2<TTool>
where TTool : IInterface1<IValue>
{
public TTool SomeTool { get; set; }
}
public class RealizeInterface2 : RealizeInterface2Base<RealizeInterface1>
{
}
}
Получаем следующую ошибку:
The type 'PokerDecisions.ExampleHierarchy.RealizeInterface1' must be convertible to 'PokerDecisions.ExampleHierarchy.IInterface1<PokerDecisions.ExampleHierarchy.IValue>' in order to use it as parameter 'TTool' in the generic class 'PokerDecisions.ExampleHierarchy.RealizeInterface2Base<TTool>'
Моя иерархия ещё более запутанная и сложная: у меня есть ещё одно вложенное наследование всех этих иерархий, и получается аж два неявно закрытых дженерика, а не один, как здесь.
Что меня озадачивает: класс RealizeInterface1 реализует интерфейс IInterface1 и закрывает дженерик, но он "неконвертируемый", как пишет компилятор.
Если я вместо него подставляю RealizeInterface1Base<ValueClass> и также с базовым ValueClassBase, то получу ровно ту же ошибку, однако если вместо ValueClass подставить IValue, то всё магическим образом становится конвертируемым.
public class RealizeInterface2 : RealizeInterface2Base<RealizeInterface1Base<IValue>>
{
}
Делаю вывод, что ограничение в виде интерфейса, который сам является ограничением, работает как-то не совсем так, как я ожидал, но уже второй день ломаю мозги, как же будет правильно? Очевидным кажется выход для IValue также добавить параметр типа, тогда всё вылечится, например:
public interface IInterface2<TTool, TValue>
where TTool : IInterface1<TValue>
where TValue : IValue
{
TTool SomeTool { get; set; }
}
public abstract class RealizeInterface2Base<TTool, TValue> : IInterface2<TTool, TValue>
where TTool : IInterface1<TValue>
where TValue : IValue
{
public TTool SomeTool { get; set; }
}
public class RealizeInterface2 : RealizeInterface2Base<RealizeInterface1, ValueClass>
{
}
Всё компилируется, все счастливы, кроме меня. Зачем мне наследник, закрывающий дженерик, если я всё равно вынужден явно смотреть, каким классом закрыт этот дженерик и явно закрывать его во всей иерархии?
Здесь это не выглядит так уж страшно, но мне нужно прошерстить пару десятков файлов и изменить ограничения, а при каждом новом файлике посмотреть из чего состоят свойства классов, которыми я закрыл верхний дженерик, и это сейчас у меня "всего" три параметра типа, а если где-то в середине объявится ещё один дженерик, нужно будет вверх по иерархии передать ещё один тип?
И ещё не нравится, что я как бы создал наследника, у которого закрыты параметры типов, и вынужден явно рядом с ним приписывать тот же тип, которым только что в нём закрыл этот параметр...
Нет ли какого-либо более изящного выхода? Или может я вообще что-то не так применяю?