Создание вариантных универсальных интерфейсов (C#)

Создание вариантных универсальных интерфейсов (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 объявлен ковариантным в одном интерфейсе, его нельзя объявить контравариантным в расширенном интерфейсе и наоборот. Это показано в следующем примере кода.

Недопущение неоднозначности

При реализации вариантных универсальных интерфейсов вариативность может приводить к неоднозначности. Такой неоднозначности следует избегать.

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

📎📎📎📎📎📎📎📎📎📎