Дженерики и литье

class B : A {}
class Sub<T> where T:A
{
//...
}

Я хочу хранить Sub-экземпляры в коллекции.

var c = new List<Sub<A>>();
c.Add(new Sub<B>()); //doesn't work

Однако, чтобы заставить его работать, я должен объявить интерфейс и хранить экземпляры этого интерфейса.

interface IBase
{
    void DoStuff(A a);
}

var c = new List<IBase>();
c.Add(new Sub<B>()); //works

Есть ли более элегантный способ сделать это?

Ответ 1

Нет. Игнорирование родового типа с разными аргументами типа полностью не связано. Единственный способ получить их в том же списке состоит в том, что в списке используется общий базовый класс или интерфейс общего назначения, как вы это делали.

Вы можете достичь приближения того, что вы хотите, используя ковариантный общий интерфейс:

class A { }
class B : A { }

interface ISub<out T> where T : A
{
    // Members go here
}

class Sub<T> : ISub<T> where T : A
{
    // Members go here.
}

Что можно использовать следующим образом:

List<ISub<A>> list = new List<ISub<A>>();
list.Add(new Sub<B>());

Вам нужен интерфейс, потому что только интерфейсы могут иметь ковариантные или контравариантные параметры типа.

Ответ 2

В зависимости от того, как вы его используете, но, возможно, вы можете использовать ковариантный интерфейс для Sub:

interface ISub<out T> where T:A
{
}

Тогда ISub<B> может быть неявно преобразован (через ссылочное преобразование) в ISub<A>