модификатор параметра out в делегате функций (C #)

#c# #.net

#c# #.net

Вопрос:

Я новичок в C #, просто вопрос по делегату функций:

 public delegate TResult Func<in T,out TResult>(T arg);
  

Я могу понять необходимость размещения in ключевого слова перед T, поскольку мы не хотим изменять исходный ввод, но как насчет out перед TResult? разве это out не означает, что нам нужно изменить вывод, но почему? разве мы иногда не генерируем возвращаемый объект «на лету», допустим, у нас есть делегат:

 Func<string, bool> nameFilter = str => str[0] == 'S';
  

итак, он проверяет строку, является ли ее первый символ ‘S’, затем возвращает true или false, поэтому мы динамически возвращаем это логическое значение, что здесь делает ключевое слово out? для возврата ничего не требуется изменять?

Комментарии:

1.Смотрите: Ковариация и контравариантность (C #).

2. Эта ссылка актуальна, но я думаю, что различия в делегатах (на которые ссылается ваша ссылка) применимы более непосредственно

Ответ №1:

Краткий ответ

Вам редко нужно беспокоиться о in и out ключевых словах в общих определениях типов. Класс, определенный с помощью параметров in / out универсального типа, обычно «просто работает», когда вы его используете, и я бы поспорил, что большинство разработчиков никогда не напишут такое определение в своем собственном коде.

Более длинный ответ

Чтобы получить полное объяснение, вам следует прочитать о ковариации и контравариантности и дисперсии в Delegates. Остальная часть моего ответа — это всего лишь несколько иллюстративных примеров кода.

Чтобы упростить объяснение, я собираюсь объяснить in и out отдельно через Action<T> и Func<TResult> вместо Func<T,TResult> .

Во всех примерах используются следующие два класса:

 class BaseClass {}
class DerivedClass : BaseClass {}
  

Ковариация: out

Для этого примера я имитировал Func<out TResult> , но удалил out модификатор (ковариация), чтобы продемонстрировать его эффект. Ковариация позволяет нам использовать функцию, которая возвращает DerivedType в любом месте, где ожидается функция, которая возвращает BaseType .

 class CovarianceExamples
{
    // This is similar to System.Func<out TResult>(), but with covariance removed
    delegate TResult InvariantFunc<TResult>(); 

    void InvariantFuncExample()
    {
        // Ignore the values of these variables; it's the types that are important
        InvariantFunc<BaseClass> baseFunc = null;
        InvariantFunc<DerivedClass> derivedFunc = null;

        baseFunc = baseFunc; // Allowed
        baseFunc = derivedFunc; // Not allowed; compile error!
    }

    void CovariantFuncExample()
    {
        // Ignore the values of these variables; it's the types that are important
        Func<BaseClass> baseFunc = null;
        Func<DerivedClass> derivedFunc = null;

        baseFunc = baseFunc; // Allowed
        baseFunc = derivedFunc; // Allowed
    }
}
  

Контравариантность: in

Для этого примера я имитировал Action<in T> , но удалил in модификатор (контравариантность), чтобы продемонстрировать его эффект. Контравариантность позволяет нам использовать действие, которое принимается BaseType везде, где ожидается действие, которое принимает DerivedType .

 class ContravarianceExamples
{
    // This is similar to System.Action<in T>(T), but with contravariance removed
    delegate void InvariantAction<T>(); 

    void InvariantActionExample()
    {
        // Ignore the values of these variables; it's the types that are important
        InvariantAction<BaseClass> baseAction = null;
        InvariantAction<DerivedClass> derivedAction = null;

        baseAction = baseAction; // Allowed
        derivedAction = baseAction; // Not allowed; compile error!
    }

    void ContravariantActionExample()
    {
        // Ignore the values of these variables; it's the types that are important
        Action<BaseClass> baseAction = null;
        Action<DerivedClass> derivedAction = null;

        baseAction = baseAction; // Allowed
        derivedAction = baseAction; // Allowed
    }
}