Какова цель ключевого слова out у вызывающего абонента (в С#)?

Если у функции C# есть выходной параметр, это можно сделать следующим образом:

private void f(out OutputParameterClass outputParameter);

Это означает, что параметр не нужно инициализировать при вызове функции. Однако при вызове этой функции вы должны повторить ключевое слово out:

f(out outputParameter);

Мне интересно, для чего это хорошо. Почему необходимо повторять часть спецификации функции? Кто-нибудь знает?


person Dimitri C.    schedule 08.09.2009    source источник


Ответы (8)


Это означает, что вы знаете, что делаете — что вы признаете, что это параметр out. Вы действительно хотите, чтобы совершенно другое поведение происходило незаметно? Кстати, то же самое верно и для ref.

(Вы также можете перегрузить на основе значения по сравнению с выходом/ссылкой, но я бы не рекомендовал это.)

По сути, если у вас есть (незахваченная) локальная переменная и вы используете ее в качестве аргумента, отличного от out/ref, вы знаете, что значение этой переменной не будет изменено внутри метода. (Если это переменная ссылочного типа, то данные внутри объекта, на который она ссылается, могут быть изменены, но это совсем другое.)

Это позволяет избежать ситуации, которая возникает в C++, когда вы неосознанно передаете что-то по ссылке, но предполагаете, что значение не изменилось...

person Jon Skeet    schedule 08.09.2009
comment
Точно. Это может вызвать огромные головные боли, если ваша переменная бессознательно изменена вызываемым пользователем. - person mmx; 08.09.2009
comment
Здесь, здесь! Возврат PHP по ссылке (или передача параметров по ссылке, то же самое) позволяет игнорировать по ссылке, исключая амперсанд в точке назначения возвращаемого значения. Поговорите о безумно сводящей с ума ошибке, когда все, что вы упустили, это где-то амперсанд. Требование в C# — самый разумный способ избежать таких ошибок. - person Colin Burnett; 08.09.2009
comment
Как любопытство, почему не рекомендуется иметь перегрузку на выходе? Влияет ли это на производительность или для удобочитаемости? - person Pierre-Alain Vigeant; 08.09.2009
comment
@Mehrdad: в C# все экземпляры класса передаются по ссылке. - person Dimitri C.; 08.09.2009
comment
Это позволяет избежать ситуации, с которой вы сталкиваетесь в C++, когда вы неосознанно передаете что-то по ссылке: в C++ большинство параметров функций должны быть помечены как константы. В таком случае вы (почти) уверены, что ваша переменная не будет изменена. - person Dimitri C.; 08.09.2009
comment
@Dimitri: Нет, это не так. Ссылка передается по значению. Есть огромная разница. Прочтите pobox.com/~skeet/csharp/parameters.html. - person Jon Skeet; 08.09.2009
comment
@Dimitri: Вы знаете о const только в том случае, если проверяете подпись, и в этом случае вы уже знаете, что это по ссылке. - person Jon Skeet; 08.09.2009
comment
Dimitri C: Процитирую Джона: Объекты вообще не передаются. Ссылки передаются по значению. - person mmx; 08.09.2009
comment
@Pierre-Alain: Перегрузка и так может сбить с толку. Перегрузка такой относительно тонкой вещью, как способ передачи параметра, почти неизбежно вызовет путаницу. - person Jon Skeet; 08.09.2009
comment
Если необходимо дублировать ключевое слово out, не лучше ли дублировать и типы параметров? - person Dimitri C.; 08.09.2009
comment
@Dimitri: они уже применяются компилятором с помощью статической типизации - тип аргумента должен быть неявно преобразован в тип параметра, или в случае ref/out они должны быть точно такого же типа. - person Jon Skeet; 08.09.2009
comment
САМАЯ важная причина повторения out/ref заключается в том, что если функция, которую вы вызываете, подвергнется рефакторингу с другой сигнатурой, вы получите ошибку компиляции. В частности, если параметр переходит из состояния non-out в состояние out, вы сразу об этом узнаете. - person plinth; 08.09.2009
comment
@Jon, @Dimitri C.: Кроме того, читатели вызывающего кода могут не знать об out/ref в сигнатуре функции. С другой стороны, типы параметров легко вывести, зная типы фактических параметров. - person Daniel Daranas; 08.09.2009
comment
@plinth: это очень хорошее замечание. Я не думал об этом! Не лучше ли опубликовать это как ответ? - person Dimitri C.; 08.09.2009
comment
На мой взгляд, если это дублирование служит только для ясности, оно просто увеличивает многословие. Тем не менее, я думаю, что замечание plinth уместно, поскольку оно помогает рефакторингу. - person Dimitri C.; 08.09.2009
comment
@Dimitri C. Я думаю, что это действительно увеличивает ясность. - person Daniel Daranas; 08.09.2009
comment
@Dimitri: Имейте в виду, что это дублирование (часто) не происходит между двумя битами кода в одном файле. Это между параметром метода, для которого у вас может даже не быть источника, и аргументом метода, на который вы смотрите. Лично мне нравится понимать, что будет делать код, без необходимости искать каждую подпись, чтобы проверить, есть ли у нее какие-либо параметры ref/out. - person Jon Skeet; 08.09.2009

Это конструктивная особенность. Понятно, что в этом не было необходимости, но это помогает удобочитаемости.

person Henk Holterman    schedule 08.09.2009

Хотя я не знаю происхождения такого решения, я знаю, что оно предназначено для перегрузки.

Совершенно законно создавать эти две функции в одном классе:

private void f(out OutputParameterClass outputParameter);

и

private void f(OutputParameterClass outputParameter);

Указание ключевого слова out при вызове такой перегрузки имеет смысл.

person Pierre-Alain Vigeant    schedule 08.09.2009

Для удобочитаемости, зная, что метод может/будет делать с вашей переменной.

Получил дополнительную информацию из MSDN: http://msdn.microsoft.com/en-us/vcsharp/aa336814.aspx

Вызывающий метод, который принимает выходной параметр, не обязан присваивать значение переменной, переданной в качестве выходного параметра до вызова; однако вызываемый объект должен присвоить параметру out перед возвратом.

person Zenuka    schedule 08.09.2009

Единственная причина, по которой я вижу, заключается в том, чтобы убедиться, что пользователь функции знает, что значение этого параметра может быть изменено функцией. Я думаю, это хорошо.

person mberube.Net    schedule 08.09.2009
comment
Его можно не только изменить — он будет изменен. - person Daniel Daranas; 08.09.2009
comment
Дэниел: Не совсем. Он может оставить функцию без изменений. out обеспечивается компилятором. Если объявляющая функция написана на другом языке, она может предпочесть не применять определенное присваивание. - person mmx; 08.09.2009
comment
На другом языке? Который из? - person Daniel Daranas; 08.09.2009
comment
Например, вы создаете свою функцию в IL и украшаете ее атрибутом out. Затем у вас будет вызов метода С#. - person mmx; 08.09.2009
comment
Как типично:] Ну, в любом случае, параметр out в C# (который был тегом вопроса) должен быть назначен в функции. Конечно, вы можете попытаться обойти это, но я не вижу особого смысла в использовании такого рода модификаторов (out, ref) без учета их семантики. - person Daniel Daranas; 08.09.2009

Я думаю, что это вопрос последовательности и ясности.

Ясно, что компилятор мог бы обойтись и без него. Однако с добавлением ключевого слова out ваши намерения становятся ясными, а код становится более четким и разборчивым.

person Tor Haugen    schedule 08.09.2009

Лучший ответ, который я получил, был опубликован в виде комментария plinth:

Самая важная причина повторения out/ref заключается в том, что если функция, которую вы вызываете, подвергается рефакторингу с другой сигнатурой, вы получите ошибку компиляции. В частности, если параметр переходит из состояния non-out в состояние out, вы сразу об этом узнаете.

person Dimitri C.    schedule 16.11.2009

Вам, вероятно, придется использовать out для ясности. Если бы вы не знали, не глядя на сигнатуру метода.

person Daniel    schedule 08.09.2009
comment
Пример, который у вас есть, не работает из-за определенных различий в назначении между out и ref. - person Jon Skeet; 08.09.2009