модификатор параметра out в делегате Func (C #)

2

Я новичок в С#, просто вопрос о делегате Func:

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

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

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

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

Теги:

1 ответ

5
Лучший ответ

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

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

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

Чтобы получить полное объяснение, вы должны прочитать Covariance и Contravariance and Variance in Delegates. Остальная часть моего ответа - просто пример иллюстративного кода.

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

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

class BaseClass {}
class DerivedClass : BaseClass {}

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

В этом примере я имитировал Func<out TResult>, но удалил модификатор out (ковариация), чтобы продемонстрировать его эффект. Covariance позволяет нам использовать функцию, которая возвращает 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 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 the types that are important
        Func<BaseClass> baseFunc = null;
        Func<DerivedClass> derivedFunc = null;

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

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

Для этого примера я имитировал Action<in T>, но удалил модификатор in (contravariance), чтобы продемонстрировать его эффект. Contravariance позволяет нам использовать действие, которое принимает 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 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 the types that are important
        Action<BaseClass> baseAction = null;
        Action<DerivedClass> derivedAction = null;

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню