В C # out
ключевое слово может использоваться двумя различными способами.
В качестве модификатора параметра, в котором аргумент передается по ссылке
class OutExample { static void Method(out int i) { i = 44; } static void Main() { int value; Method(out value); // value is now 44 } }
В качестве модификатора параметра типа указать ковариацию .
// Covariant interface. interface ICovariant<out R> { } // Extending covariant interface. interface IExtCovariant<out R> : ICovariant<R> { } // Implementing covariant interface. class Sample<R> : ICovariant<R> { } class Program { static void Test() { ICovariant<Object> iobj = new Sample<Object>(); ICovariant<String> istr = new Sample<String>(); // You can assign istr to iobj because // the ICovariant interface is covariant. iobj = istr; } }
Мой вопрос: почему?
Для новичка связь между ними не кажется интуитивной . Использование с генериками, похоже, не имеет ничего общего с передачей по ссылке.
Сначала я узнал, что out
было связано с передачей аргументов по ссылке, и это помешало моему пониманию использования определения ковариации с генериками.
Есть ли связь между этими видами использования, которые я пропускаю?
c#
terminology
language-design
keywords
Роуэн Фриман
источник
источник
System.Func<in T, out TResult>
делегате .Ответы:
Есть связь, но она немного свободна. В C # ключевые слова «in» и «out» в качестве названия предлагают обозначать ввод и вывод. Это очень ясно в случае выходных параметров, но менее понятно, что это нужно делать с параметрами шаблона.
Давайте посмотрим на принцип замещения Лискова :
Посмотрите, как контравариантность связана с вводом, а ковариация связана с выходом? В C # если вы пометите переменную шаблона с помощью,
out
чтобы сделать ее ковариантной, но, пожалуйста, обратите внимание, что вы можете делать это только в том случае, если упомянутый параметр типа отображается только как выходной (тип возврата функции). Таким образом, следующее недействительно:Аналогично, если вы помечаете параметр типа с помощью
in
, это означает, что вы можете использовать его только как ввод (параметр функции). Таким образом, следующее недействительно:Подводя итог, можно
out
сказать , что связь с ключевым словом заключается в том, что с параметрами функции это означает, что это выходной параметр, а для параметров типа это означает, что тип используется только в выходном контексте.System.Func
Это также хороший пример того, что Руонг упомянул в своем комментарии. ВоSystem.Func
всех входных параметров ар flaged сin
, а выходной параметр flaged сout
. Причина именно то, что я описал.источник
@ Габор уже объяснил связь (контравариантность для всего, что идет «внутрь», ковариантность для всего, что идет «снаружи»), но зачем вообще повторно использовать ключевые слова?
Ну, ключевые слова очень дороги. Вы не можете использовать их в качестве идентификаторов в своих программах. Но в английском языке очень много слов. Поэтому иногда вы сталкиваетесь с конфликтами, и вам приходится неловко переименовывать свои переменные, методы, поля, свойства, классы, интерфейсы или структуры, чтобы избежать столкновения с ключевым словом. Например, если вы моделируете школу, что вы называете классом? Вы не можете назвать это классом, потому что
class
это ключевое слово!Добавление ключевого слова на язык еще более дорогим. Это в основном делает весь код, который использует это ключевое слово как идентификатор, недопустимым, нарушая обратную совместимость повсюду.
in
Иout
ключевые слова уже существует, так что они могли просто быть использованы повторно.Они могли бы добавить контекстные ключевые слова, которые являются только ключевыми словами в контексте списка параметров типа, но какие ключевые слова они бы выбрали?
covariant
аcontravariant
?+
и-
(как, например, Scala)?super
иextends
как Java сделал? Не могли бы вы вспомнить, какие параметры являются ковариантными и контравариантными?В текущем решении есть хорошая мнемоника: параметры выходного типа получают
out
ключевое слово, параметры входного типа получаютin
ключевое слово. Обратите внимание на хорошую симметрию с параметрами метода: выходные параметры получаютout
ключевое слово, входные параметры получаютin
ключевое слово (ну, вообще-то, никакого ключевого слова вообще нет, так как ввод по умолчанию, но вы понимаете).[Примечание: если вы посмотрите на историю изменений, вы увидите, что я фактически поменял их в своем вступительном предложении. И я даже получил голос за это время! Это просто показывает, насколько важен этот мнемоник.]
источник
interface Accepter<in T> { void Accept(T it);};
, aAccepter<Foo<T>>
будет приниматьT
в качестве входного параметра, еслиFoo<T>
принимает его в качестве выходного параметра, и наоборот. Таким образом, против -variance. В противоположность этому , будет иметь какой бы то ни дисперсионный есть - таким образом , совместное -variance.interface ISupplier<out T> { T get();};
Supplier<Foo<T>>
Foo