0Pricing
C# Academy · Урок

Вариативность (in/out) с интерфейсами и делегатами

Изучите вариативность C#: out используется для производителей (ковариантность), а in — для потребителей (контравариантность) в интерфейсах и делегатах.

«Вариативность (in/out) с интерфейсами и делегатами» — бесплатный урок C# Academy на CoddyKit. Это урок 2 из 3. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 3 уроков всего.

Обзор вариантности

Вариантность позволяет обобщённым интерфейсам и делегатам работать с наследованием.

  • выходной параметр = ковариантность (производитель)
  • входной параметр = контравариантность (потребитель)
  • Это безопасно, когда интерфейс только создаёт или только принимает значения типа T

Демонстрация ковариантности

IEnumerable<out T> ковариантен: последовательность кошек можно использовать там, где ожидается последовательность животных.

using System;
using System.Collections.Generic;

public class Animal { public virtual string Name() { return "animal"; } }
public class Cat : Animal { public override string Name() { return "cat"; } }

public class Program
{
  static void PrintAll(IEnumerable<Animal> animals)
  {
    foreach (Animal a in animals)
    {
      Console.WriteLine(a.Name());
    }
  }

  public static void Main(string[] args)
  {
    List<Cat> cats = new List<Cat>();
    cats.Add(new Cat());
    // IEnumerable<Cat> -> IEnumerable<Animal> is allowed (out T)
    PrintAll(cats);
  }
}

Демонстрация контравариантности

IComparer<in T> контравариантен: средство сравнения для Animal также работает с Cat, поскольку оно только принимает T.

using System;
using System.Collections.Generic;

public class Animal { public string N; public Animal(string n){ N=n; } }
public class Cat : Animal { public Cat(string n) : base(n) {} }

// Comparer can compare any Animals
public class AnimalNameComparer : IComparer<Animal>
{
  public int Compare(Animal x, Animal y)
  {
    return string.Compare(x == null ? "" : x.N, y == null ? "" : y.N, StringComparison.Ordinal);
  }
}

public class Program
{
  public static void Main(string[] args)
  {
    List<Cat> cats = new List<Cat>();
    cats.Add(new Cat("Miso"));
    cats.Add(new Cat("Ada"));
    // IComparer<Animal> can be used as IComparer<Cat> (in T)
    cats.Sort(new AnimalNameComparer());
    foreach (Cat c in cats) Console.WriteLine(c.N);
  }
}

Вариативность делегатов

Делегаты также поддерживают вариативность: Func<out TResult> (производитель) и Action<in T> (потребитель).

using System;

public class Animal { public virtual string Speak(){ return "???"; } }
public class Cat : Animal { public override string Speak(){ return "meow"; } }

public class Program
{
  static Animal MakeAnimal() { return new Animal(); }
  static void HandleAnimal(Animal a) { Console.WriteLine(a.Speak()); }

  public static void Main(string[] args)
  {
    // Func<Cat> can be used where Func<Animal> is expected? Yes, because TResult is out.
    Func<Animal> maker = new Func<Cat>(delegate { return new Cat(); });
    Console.WriteLine(maker().Speak());

    // Action<Animal> can be used where Action<Cat> is expected? Yes, because T is in.
    Action<Cat> catSink = new Action<Animal>(HandleAnimal);
    catSink(new Cat());
  }
}

Пользовательские вариантные интерфейсы

Правило проектирования: помечайте параметры, предназначенные только для производства данных, как out, а параметры, предназначенные только для потребления, как in, чтобы включить безопасные преобразования.

using System;

// Producer-only interface: out T
public interface ISource<out T>
{
  T Get();
}

// Consumer-only interface: in T
public interface ISink<in T>
{
  void Put(T item);
}

public class Animal { public string N; }
public class Cat : Animal { }

public class CatSource : ISource<Cat>
{
  public Cat Get() { return new Cat(); }
}

public class AnimalSink : ISink<Animal>
{
  public void Put(Animal a) { Console.WriteLine("Got animal"); }
}

public class Program
{
  public static void Main(string[] args)
  {
    ISource<Animal> animals = new CatSource(); // out T allows upcast
    ISink<Cat> cats = new AnimalSink();        // in T allows down-assign
    animals.Get();
    cats.Put(new Cat());
  }
}

Советы по вариативности

Советы:

  • Параметры out могут появляться только в выходных позициях (типах возвращаемых значений и ковариантных позициях).
  • Параметры in могут появляться только во входных позициях (параметрах методов).
  • При объявлении вариативности не смешивайте производство и потребление для одного и того же T.
  • Вариативность применяется к интерфейсам и делегатам, но не к классам.

Ключевое слово ковариантности

Быстрая проверка: какое ключевое слово обозначает ковариантный параметр типа в интерфейсе или делегате?

Повторение

Повторение: используйте out для производителей (ковариантность) и in для потребителей (контравариантность) в интерфейсах и делегатах. Проектируйте API однонаправленными, чтобы включить безопасные преобразования.

Часто задаваемые вопросы

Урок «Вариативность (in/out) с интерфейсами и делегатами» бесплатный?

Да — полный текст урока «Вариативность (in/out) с интерфейсами и делегатами» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 3 уроков всего.

Чему я научусь в уроке «Вариативность (in/out) с интерфейсами и делегатами»?

Изучите вариативность C#: out используется для производителей (ковариантность), а in — для потребителей (контравариантность) в интерфейсах и делегатах. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C# Academy?

Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 3.

Сколько времени занимает урок «Вариативность (in/out) с интерфейсами и делегатами»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C# Academy?

Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Обобщённые методы и типы; ограничения
  2. Вариативность (in/out) с интерфейсами и делегатами
  3. Ссылочные типы, допускающие null (обзор), и аннотации (шаблоны C# 6)
← Назад к C# Academy