#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
}
}