Ковариантность и контравариантность — это механизмы, позволяющие гибко работать с обобщенными интерфейсами и делегатами в C#. Они определяют, как можно передавать и возвращать объекты разных типов в контексте наследования.
Ковариантность
Ковариантность (out
) позволяет использовать производный тип вместо базового при возврате значений из метода.
Пример ковариантности с интерфейсом:
interface ICovariant<out T>
{
T GetItem();
}
class Base {}
class Derived : Base {}
class CovariantExample : ICovariant<Derived>
{
public Derived GetItem() => new Derived();
}
class Program
{
static void Main()
{
ICovariant<Base> baseItem = new CovariantExample();
Console.WriteLine(baseItem.GetItem().GetType());
}
}
Ковариантность применяется только к выходным (out
) параметрам, так как передача значений обратно вызывает проблемы с безопасностью типов.
Контравариантность
Контравариантность (in
) позволяет использовать базовый тип вместо производного при передаче параметров в метод.
Пример контравариантности с интерфейсом:
interface IContravariant<in T>
{
void SetItem(T item);
}
class Base {}
class Derived : Base {}
class ContravariantExample : IContravariant<Base>
{
public void SetItem(Base item) => Console.WriteLine(item.GetType());
}
class Program
{
static void Main()
{
IContravariant<Derived> derivedItem = new ContravariantExample();
derivedItem.SetItem(new Derived());
}
}
Контравариантность применяется только к входным (in
) параметрам, так как использование производного типа вместо базового может привести к ошибкам типов.
Ковариантность и контравариантность в делегатах
Делегаты также поддерживают ковариантность и контравариантность:
delegate Base CovariantDelegate();
delegate void ContravariantDelegate(Derived d);
class Program
{
static Derived GetDerived() => new Derived();
static void ProcessBase(Base b) => Console.WriteLine(b.GetType());
static void Main()
{
CovariantDelegate covariant = GetDerived;
ContravariantDelegate contravariant = ProcessBase;
Console.WriteLine(covariant().GetType());
contravariant(new Derived());
}
}
Ковариантность и контравариантность позволяют использовать более гибкие структуры в обобщенных интерфейсах и делегатах, обеспечивая безопасность типов и удобство работы с наследуемыми классами.