В чем разница между параметрами var и out?

В чем разница между параметрами, объявленными с помощью var, и параметрами, объявленными с помощью out? Как компилятор обрабатывает их по-разному (например, генерируя другой код или изменяя диагностику, которую он выдает)? Или разные модификаторы просто позволяют программисту задокументировать предполагаемое использование параметров? Какое влияние на проблему оказывают типы параметров?


person Rob Kennedy    schedule 24.01.2013    source источник
comment
У меня всегда был один и тот же вопрос, но я никогда не удосужился его задать.   -  person Jerry Dodge    schedule 24.01.2013
comment
Я тоже. Я действительно столкнулся с этой проблемой, поэтому мне нужен действительно хороший ответ на нее.   -  person Glenn1234    schedule 24.01.2013
comment
Документация делает это довольно ясно, IMO: docwiki.embarcadero.com/RADStudio/ XE3 / ru /   -  person ain    schedule 24.01.2013
comment
@ain Это понятно. Это просто неправильно. Это верно только для управляемых типов.   -  person David Heffernan    schedule 24.01.2013
comment
возможно, вопрос в Embarcadero будет иметь значение ...   -  person RBA    schedule 25.01.2013
comment
Даже для неуправляемых типов он действует как форма документации, сообщая другим программистам, что любое входное значение будет проигнорировано.   -  person Gerry Coll    schedule 25.01.2013
comment
@Gerry Но входное значение не игнорируется. Он передается в функцию. Функция может его использовать.   -  person David Heffernan    schedule 25.01.2013
comment
@David, функция может использовать входное значение параметра out, но не должна, потому что, объявив параметр out, функция обещала не использовать его таким образом. Я всегда думал, что компилятор должен предупреждать, когда функция считывает выходной параметр перед назначением ему, как это происходит с локальными переменными. Точно так же я думаю, что он должен предупреждать, когда вызывающий объект присваивает значение переменной непосредственно перед передачей его функции в качестве параметра out.   -  person Rob Kennedy    schedule 25.01.2013
comment
@Rob Мне лично нравится out способ C #. Считывание выходного параметра до его полной инициализации недопустимо. И функция не может вернуться, если она полностью не инициализировала ее. И C # также получает правильную семантику возврата функций, что является еще одной моей ошибкой.   -  person David Heffernan    schedule 25.01.2013
comment
@DavidHeffernan - но если он объявлен как out, он не должен (а функции не должны иметь побочных эффектов и различных других часто нарушаемых правил :-)). Это нарушение подразумеваемого договора.   -  person Gerry Coll    schedule 26.01.2013
comment
@GerryColl Да, компилятор Delphi позволяет программисту разорвать контракт. Компилятор C # этого не делает.   -  person David Heffernan    schedule 26.01.2013


Ответы (3)


Параметр var будет передан по ссылке, и все.

Параметр out также передается по ссылке, но предполагается, что входное значение не имеет значения. Для управляемых типов (строки, интерфейсы и т. Д.) Компилятор будет обеспечивать это, очищая переменную перед запуском подпрограммы, что эквивалентно записи param := nil. Для неуправляемых типов компилятор реализует out идентично var.

Обратите внимание, что очистка управляемого параметра выполняется на сайте вызова, и поэтому код, сгенерированный для функции, не зависит от параметров out или var.

person Mason Wheeler    schedule 24.01.2013
comment
Этот ответ был красивым и простым, но теперь я прочитал (удаленный) ответ Серга и снова запутался. Он говорит, что out важен для параметров интерфейса внешних функций, потому что другой (не Delphi) код может вызвать его с мусорным значением. Но если единственная разница между var и out заключается в том, как компилятор обрабатывает вызывающую сторону, а вызывающая сторона не находится в Delphi в сценарии Serg, тогда я не понимаю, в чем различие Serg пытаюсь указать. - person Rob Kennedy; 25.01.2013
comment
@ Роб, я думаю, что Серг здесь немного запутался. Он понял это прямо в своем блоге: sergworks.wordpress.com/2012/10/01/ - person David Heffernan; 25.01.2013
comment
Чем больше я думаю о том, почему были реализованы out params, тем больше мне интересно. Нет необходимости обнулять параметр out вне процедуры в собственном коде Delphi, и я не мог понять, как текущая реализация параметров out может помочь навести мост к другим языкам, которые по-разному трактуют параметры out типа интерфейса. - person kludg; 25.01.2013
comment
@Serg, если другие языки не очищают выходной параметр перед назначением ему, а Delphi не очищает его перед вызовом, то это утечка памяти. Delphi не знает, что будет делать вызываемая функция, поэтому единственный безопасный выбор - всегда очищать переменную перед передачей ее другому коду. Лучше дважды очищать переменную, чем не очищать вовсе. - person Rob Kennedy; 25.01.2013
comment
Хорошо, я читаю здесь все ответы и комментарии и до сих пор не понимаю эту тему, в частности, что я спрашивал в связи с ссылкой, в частности, почему мое определение SHChangeNotification_Lock требует 'out' в параметрах для работы и не будет работать с 'var'? Я предполагаю, что управляемые типы имеют какое-то отношение к .NET, но в этом определении присутствует тип longint. Итак, var не очищает входное значение при передаче его функции, а out - делает? - person Glenn1234; 25.01.2013
comment
@ Glenn1234: Управляемые типы не имеют ничего общего с .NET. Это относится к типам данных, в которые компилятор добавляет специальный код для управления памятью, например строки, динамические массивы и интерфейсы. - person Mason Wheeler; 25.01.2013
comment
@RobKennedy +1 возможная утечка памяти на стороне Delphi - достаточное объяснение, спасибо. - person kludg; 25.01.2013
comment
@masonwheeler Хорошо, а каков ответ на вопрос? Если разница между var и out не более чем бухгалтерская (что я понял из всего этого), почему я заметил то, что наблюдаю? Должна быть веская причина, по которой var не работал в этом определении, а работал. - person Glenn1234; 26.01.2013
comment
@Glenn: Понятия не имею. Похоже, в вашем ответе было несколько моментов. Если вы возьмете свой ответ, который, по-видимому, сейчас работает, и измените out на var, но оставите все остальное без изменений, перестанет ли он работать? - person Mason Wheeler; 26.01.2013
comment
@ Glenn1234 Ваше определение SHChangeNotification_Lock неверно. Это описано в отчете о контроле качества. Изменение out на var для объявления в этом ответе ничего не меняет. Вы можете посмотреть на сгенерированный код и убедиться, что он идентичен. Не знаю, почему вы там приняли свой ответ, потому что он в корне неверен, по крайней мере, в отношении SHChangeNotification_Lock. - person David Heffernan; 26.01.2013
comment
Какой в ​​итоге смысл этой функциональности? есть ли какая-либо экономия памяти / процессора при использовании out? - person hikari; 25.01.2017

Для компилятора особой разницы нет. См. ответ Мейсона для этого.

Семантически большая разница:

  • var сообщает программисту, что подпрограмма может работать со своим текущим значением,
  • out сообщает программисту, что подпрограмма проигнорирует / отбросит свое текущее значение.
person NGLN    schedule 25.01.2013
comment
@David, этот ответ касается документации, а не реального поведения. Я думаю, что большинство людей обычно предполагают, что out в Delphi работает так же, как out в C #, где незаконно читать параметр до его записи. Когда люди объявляют out параметры в Delphi, я думаю, что обычно они намереваются относиться к ним именно так. Использование out вместо var служит для документирования этого намерения, даже если компилятор на самом деле не применяет его. Считывание параметра out перед записью в него является ошибкой и ошибкой, даже если компилятор не помечает это как ошибку. - person Rob Kennedy; 25.01.2013
comment
@Rob Если компилятор не проверит, рушится все здание. Вы, программист, должны пойти и проверить функцию перед ее вызовом. Я имею в виду, что при написании функции я использую out для документирования своего намерения. Но когда я сталкиваюсь с функцией с параметром out, мне нужно пойти и проверить. - person David Heffernan; 25.01.2013
comment
@ Дэвид. Ты не понимаешь. Я имею в виду, что объявление, а значит, и значение, отличаются для пользователя подпрограммы; дело не во внутреннем устройстве. Параметр, объявленный как out, говорит программисту, что бесполезно подготавливать то, чем вы его кормите. Процедура с параметром var требует подготовки и / или рассмотрения, out - нет. Посоветуйте, пожалуйста, как переписать мой ответ, чтобы это было более ясно. - person NGLN; 25.01.2013
comment
@NGLN Я полностью понимаю. Я полностью понимаю семантику out. Просто, поскольку компилятор не применяет его, функция вполне может использовать значение, которое я вводю. Другими словами, автор подпрограммы может лгать мне, и компилятор не жалуется. Представьте, что компилятор не позаботился о обеспечении безопасности типов. Только потому, что функция сообщила, что ожидает получить параметр одного типа, ошибочный программист может отправить что-то еще. - person David Heffernan; 25.01.2013
comment
Проблема IMO в том, что документация о том, как Delphi обрабатывает out параметры, вероятно, была написана для Delphi.NET или еще, но не для Delphi. Компилятор Delphi просто не может реализовать out параметры в соответствии с документацией, и это вызывает вопросы, что такое out параметры на самом деле и когда их следует использовать. - person kludg; 25.01.2013
comment
Я думаю, что если программист использует это значение, он заявляет, что он не будет читать значение и считать его полезным. Я лично проверяю, правильно ли кто-то написал распорядок, только если у меня есть проблемы. Так что я думаю, что ответ NGLN верен. - person Dsm; 16.09.2015

Немного поздно, но для протокола, я столкнулся со случаем, когда var или out имели большое значение.

Я работал над веб-службой SOAP, которая экспортировала следующий метод:

function GetUser( out User :TUser ) :TResult;

который импортировался в C # как эквивалент

function GetUser( out Result :TResult) :TUser;

когда я изменил out на var, он импортировал правильно.

Я предполагаю, что вызывающая программа Delphi SOAP обрабатывает результат функции как параметр out и что наличие двух параметров out сбивает с толку процедуры SOAP Delphi. Я не уверен, есть ли обходной путь, позволяющий использовать параметры out.

person Steve    schedule 20.02.2014