Каква е разликата между 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/en/   -  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, функцията може да използва входната стойност на изходящ параметър, но не трябва, тъй като чрез декларирането на изходящ параметър функцията е обещала да не го използва по този начин. Винаги съм смятал, че компилаторът трябва да предупреждава, когато дадена функция прочете изходен параметър, преди да го присвои, точно както се прави за локални променливи. По същия начин мисля, че трябва да предупреждава, когато повикващият присвои стойност на променлива непосредствено преди да я предаде на функция като изходящ параметър.   -  person Rob Kennedy    schedule 25.01.2013
comment
@Rob Аз лично харесвам out по начина C#. Незаконно е да се чете изходящ параметър, преди да е бил напълно инициализиран. И функцията няма право да се връща, освен ако не я е инициализирала напълно. И C# получава правилна семантика за връщане на функцията, което е друг мой бъг.   -  person David Heffernan    schedule 25.01.2013
comment
@DavidHeffernan - но ако е декларирано като излязло, не трябва (и функциите не трябва да имат странични ефекти и различни други често нарушавани правила :-) ). Това е нарушение на подразбиращия се договор.   -  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
Този отговор беше хубав и прост, но сега прочетох (изтрития) отговор на Serg и отново съм объркан. Той казва, че out е важно за параметрите на интерфейса на външните функции, защото друг (не-Delphi) код може да го извика с боклук стойност. Но ако единствената разлика между var и out е в начина, по който компилаторът обработва извикващата страна, а извикващата страна не е в Delphi в сценария на Serg, тогава не разбирам каква разлика е Serg опитвайки се да изтъкна. - person Rob Kennedy; 25.01.2013
comment
@Rob Мисля, че Serg е малко объркан тук. Той го разбира правилно в своя блог: sergworks.wordpress.com/2012/10/01/ - person David Heffernan; 25.01.2013
comment
Колкото повече мисля защо out параметрите са внедрени, толкова повече се чудя. Няма нужда да се нулира параметърът 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 да не работи в тази дефиниция, а out да работи. - 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, за да документирам намерението си. Но когато попадна на функция с изходящ параметър, трябва да отида и да проверя. - person David Heffernan; 25.01.2013
comment
@David Не разбираш. Това, което имам предвид е, че de декларацията, следователно значението, се различава за потребителя на рутината; не става въпрос за вътрешностите. Параметър, деклариран като 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
Мисля, че ако програмист използва out, тогава той декларира, че няма да прочете стойността и да приеме, че е полезна. Аз лично проверявам дали някой е написал правилно рутина само ако имам проблеми. Така че мисля, че отговорът на NGLN е на място. - person Dsm; 16.09.2015

Малко късно, но само за протокола, попаднах на случай, при който var или out направиха голяма разлика.

Работех върху SOAP уеб услуга, която експортира следния метод:

function GetUser( out User :TUser ) :TResult;

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

function GetUser( out Result :TResult) :TUser;

когато промених out на var, той се импортира правилно.

Предполагам, че програмата за извикване на SOAP на Delphi третира резултата от функцията като параметър out и че наличието на два параметъра out обърква SOAP рутинните процедури на Delphi. Не съм сигурен дали има заобиколно решение, което да ви позволи да използвате out параметри.

person Steve    schedule 20.02.2014