csrss Ответов: 1

Вопрос о дженериках


Сейчас я пытаюсь немного покопаться в дженериках и не понимаю следующего примера:
interface IBaseObject
{
}
interface ITest<T> where T : class, IBaseObject
{
}
abstract class Test<T> : ITest<T> where T : class, IBaseObject
{
}
class BaseObject : IBaseObject
{
}
class ChildObject : BaseObject
{
}
class Specialized : Test<ChildObject>
{
}

class Program
{
    static void Main(string[] args)
    {
        ITest<IBaseObject> test = new Specialized() as ITest<IBaseObject>;
    }
}


ITest<IBaseObject> test
является нулевым.

Что я уже пробовал:

Я понимаю, что с этим кодом что-то не так, но не могу понять, что это может быть. Можно ли вообще привести специализированный объект к универсальному интерфейсу?
Спасибо

1 Ответов

Рейтинг:
7

Richard Deeming

Ковариация и Контравариация в универсальных шаблонах | Майкрософт документы[^]

Это не простая тема. :)

Представьте себе мир, где I<Derived> был эквивалентен I<Base> Это означало бы, что следующий код будет работать:

class Base { }
class Derived : Base { }
class OtherDerived : Base { }

List<Derived> list = new List<Derived>();
IList<Base> baseList = list;
baseList.Add(new OtherDerived());

Теперь список разбит - он может содержать только Derived объекты, но нам удалось прокрасться OtherDerived пример в него.

Для того чтобы ваш образец работал, ваш ITest<T> интерфейс должен быть ковариантным:
interface ITest<out T> where T : class, IBaseObject
{
}

С этим единственным изменением ваш код будет работать так, как ожидалось.

Однако теперь ваш интерфейс будет подчиняться ограничениям, наложенным на ковариантный интерфейс: вы не можете использовать параметр type в качестве параметра метода, даже если он не является параметром метода. out параметр; вы также не можете использовать его в качестве типа записываемого свойства.


OriginalGriff

В точку! :большой палец вверх:

csrss

Таким образом, кажется, что я не могу использовать текущий дизайн, потому что интерфейс (ITest) использует T как in, так и out. Спасибо

Richard Deeming

В зависимости от того, как вы его используете и что собираетесь с ним делать. ITest<Base>, вы вероятно быть в состоянии разделить интерфейс на две или, возможно, три части: ковариантную часть, контравариантную часть и инвариантную часть.

Например, IReadOnlyList<T>[^] предоставляет ковариантную версию IList<T>[^] интерфейс.

csrss

Ричард, большое тебе спасибо :)
Я действительно немного изменил свой дизайн, введя дополнительный не универсальный интерфейс, и это решило проблему.