Создание вариантных универсальных интерфейсов (C#)
Параметры универсального типа можно объявить в интерфейсах как ковариантные или контравариантные. Ковариация позволяет методам интерфейса иметь тип возвращаемого значения, степень наследования которого больше, чем указано в параметрах универсального типа. Контравариантность позволяет методам интерфейса иметь типы аргументов, степень наследования которых меньше, чем указано в параметре универсального типа. Универсальный интерфейс, который имеет ковариантные или контравариантные параметры универсального типа, называется вариантным.
В платформе .NET Framework 4 появилась поддержка вариативности для нескольких существующих универсальных интерфейсов. Список вариативных интерфейсов в .NET см. в статье Вариативность в универсальных интерфейсах (C#).
Объявление вариантных универсальных интерфейсов
Вариантные универсальные интерфейсы можно объявить с помощью ключевых слов in и out для параметров универсального типа.
Параметры ref , in и out в C# не могут быть вариантными. Типы значений также не поддерживают вариативность.
Для объявления ковариантного параметра универсального типа можно использовать ключевое слово out . Ковариантный тип должен удовлетворять следующим условиям:
Тип используется только в качестве типа значения, возвращаемого методами интерфейса, и не используется в качестве типа аргументов метода. Это показано в следующем примере, в котором тип R объявлен ковариантным.
Существует одно исключение из данного правила. Если в качестве параметра метода используется контравариантный универсальный делегат, этот тип можно использовать в качестве параметра универсального типа для этого делегата. Это продемонстрировано ниже на примере типа R . Дополнительные сведения см. в разделах Вариативность в делегатах (C#) и Использование вариативности в универсальных методах-делегатах Func и Action (C#).
Тип не используется в качестве универсального ограничения для методов интерфейса. Это демонстрируется в следующем примере кода.
Для объявления контравариантного параметра универсального типа можно использовать ключевое слово in . Контравариантный тип можно использовать только в качестве типа аргументов метода, но не в качестве типа значения, возвращаемого методами интерфейса. Контравариантный тип можно также использовать для универсальных ограничений. В следующем примере кода показано объявление контравариантного интерфейса и использование универсального ограничения для одного из его методов.
Кроме того, можно реализовать поддержку ковариации и контравариации в одном интерфейсе, но для разных параметров типа, как показано в следующем примере кода.
Реализация вариантных универсальных интерфейсов
Для реализации вариантных универсальных интерфейсов в классах используется тот же синтаксис, что и для инвариантных интерфейсов. В следующем примере кода показана реализация ковариантного интерфейса в универсальном классе.
Классы, которые реализуют вариантные интерфейсы, являются инвариантными. Например, рассмотрим следующий код.
Расширение вариантных универсальных интерфейсов
При расширении вариантных универсальных интерфейсов необходимо использовать ключевые слова in и out для явного указания того, поддерживает ли вариативность производный интерфейс. Компилятор не подразумевает вариативность интерфейса, который расширяется. Например, рассмотрим следующие интерфейсы.
В интерфейсе IInvariant<T> параметр универсального типа T является инвариантным, тогда как в IExtCovariant<out T> параметр типа является ковариантным, хотя оба интерфейса расширяют один и тот же интерфейс. То же правило применяется к контравариантным параметрам универсального типа.
Можно создать интерфейс, который расширяет и интерфейс, в котором параметр универсального типа T является ковариантным, и интерфейс, где он является контравариантным, если в расширяемом интерфейсе параметр универсального типа T является инвариантным. Это показано в следующем примере кода.
Тем не менее, если параметр универсального типа T объявлен ковариантным в одном интерфейсе, его нельзя объявить контравариантным в расширенном интерфейсе и наоборот. Это показано в следующем примере кода.
Недопущение неоднозначностиПри реализации вариантных универсальных интерфейсов вариативность может приводить к неоднозначности. Такой неоднозначности следует избегать.
Например, если вы явно реализуете один вариантный универсальный интерфейс с разными параметрами универсального типа в одном классе, это может создавать неоднозначность. Компилятор не сообщает об ошибке в данном случае, но и не указывает, какая реализация интерфейса будет выбрана во время выполнения. Такая неоднозначность может привести к возникновению неявных ошибок в коде. Рассмотрим следующий пример кода: