Я создаю функцию, в которой мне нужно передать объект, чтобы он мог быть изменен функцией. В чем разница между:
public void myFunction(ref MyClass someClass)
а также
public void myFunction(out MyClass someClass)
Что я должен использовать и почему?
MyClass
это будетclass
тип, то есть ссылочный тип. В этом случае передаваемый вами объект может быть измененmyFunction
даже без ключевого словаref
/out
.myFunction
получит новую ссылку, которая указывает на тот же объект, и он может изменять тот же объект столько, сколько он хочет. Различиеref
, которое могло бы иметь ключевое слово, заключалось бы в том, что онmyFunction
получал одну и ту же ссылку на один и тот же объект. Это было бы важно, только еслиmyFunction
бы изменить ссылку, чтобы указать на другой объект.Ответы:
ref
сообщает компилятору, что объект инициализируется перед входом в функцию, аout
компилятору сообщает, что объект будет инициализирован внутри функции.Так что пока
ref
есть два пути,out
это только для внешнего.источник
В
ref
модификатор означает , что:В
out
модификатор означает , что:источник
out
, можно ли вообще прочитать его внутри метода, до того, как он был установлен этим методом, если он был инициализирован до вызова метода? Я имею в виду, может ли вызываемый метод прочитать, что вызывающий метод передал ему в качестве аргумента?Допустим, Дом появляется в кабинете Питера о записке о докладах TPS.
Если бы Дом был аргументом в пользу ссылки, у него была бы печатная копия записки.
Если бы Дом был спорным аргументом, он заставил бы Питера напечатать новую копию памятки, которую он мог взять с собой.
источник
Я собираюсь попробовать свои силы в объяснении:
Я думаю, мы понимаем, как типы значений работают правильно? Типы значений (int, long, struct и т. Д.). Когда вы отправляете их в функцию без команды ref, она копирует данные . Все, что вы делаете с этими данными в функции, влияет только на копию, а не на оригинал. Команда ref отправляет ФАКТИЧЕСКИЕ данные, и любые изменения будут влиять на данные вне функции.
Хорошо, к запутанной части, ссылочные типы:
Давайте создадим ссылочный тип:
Когда вы создаете новый объект , создаются две части:
Теперь, когда вы отправляете некоторый объект в метод без ссылки, он КОПИРУЕТ указатель ссылки , а НЕ данные. Итак, теперь у вас есть это:
Две ссылки, указывающие на один и тот же объект. Если вы изменяете свойство в каком-либо объекте, используя reference2, это повлияет на те же данные, на которые указывает reference1.
Если вы обнулите reference2 или укажете на новые данные, это не повлияет на reference1 или на reference1.
Теперь, что происходит, когда вы отправляете некий объект по ссылке на метод? Фактическая ссылка на SomeObject отправляется к методу. Теперь у вас есть только одна ссылка на данные:
Но что это значит? Он действует точно так же, как отправка объекта не по ссылке, за исключением двух основных моментов:
1) Когда вы обнуляете ссылку внутри метода, она обнуляет ссылку вне метода.
2) Теперь вы можете указать ссылку на совершенно другое местоположение данных, и ссылка вне функции теперь будет указывать на новое местоположение данных.
источник
ref
иout
параметров.out
ключевого слова?Реф входит и выходит .
Вы должны использовать
out
в предпочтениях там, где этого достаточно для ваших требований.источник
вне:
В C # метод может возвращать только одно значение. Если вы хотите вернуть более одного значения, вы можете использовать ключевое слово out. Модификатор out возвращается как ссылка по ссылке. Самый простой ответ заключается в том, что ключевое слово «out» используется для получения значения из метода.
ссылка:
В C #, когда вы передаете тип значения, такой как int, float, double и т. Д. В качестве аргумента параметра метода, он передается по значению. Следовательно, если вы измените значение параметра, оно не повлияет на аргумент в вызове метода. Но если вы пометите параметр ключевым словом «ref», он будет отражен в фактической переменной.
источник
Расширяя Собаку, Кошачий пример. Второй метод с ref изменяет объект, на который ссылается вызывающая сторона. Отсюда и "кот" !!!
источник
Поскольку вы передаете ссылочный тип (класс), в этом нет необходимости,
ref
потому что по умолчанию передается только ссылка на реальный объект, и поэтому вы всегда меняете объект за ссылкой.Пример:
Пока вы переходите в класс, вам не нужно использовать его,
ref
если вы хотите изменить объект внутри вашего метода.источник
someObject = null
вBar
конец выполнить. Ваш код будет работать нормально, так как толькоBar
ссылка на экземпляр была обнулена. Теперь изменитеBar
наBar(ref MyClass someObject)
и выполните снова - вы получите,NullReferenceException
потому чтоFoo
ссылка на экземпляр тоже была обнулена.ref
иout
вести себя аналогично, за исключением следующих различий.ref
переменная должна быть инициализирована перед использованием.out
переменная может использоваться без присваиванияout
параметр должен рассматриваться как неназначенное значение функцией, которая его использует. Таким образом, мы можем использовать инициализированныйout
параметр в вызывающем коде, но значение будет потеряно при выполнении функции.источник
Для тех, кто учится на примере (как я), вот что говорит Энтони Колесов .
Я создал несколько минимальных примеров ref, out и других, чтобы проиллюстрировать это. Я не освещаю лучшие практики, просто примеры, чтобы понять различия.
https://gist.github.com/2upmedia/6d98a57b68d849ee7091
источник
«Бейкер»
Это потому, что первый изменяет вашу строковую ссылку, чтобы указать на «Бейкер». Изменение ссылки возможно, потому что вы передали ее через ключевое слово ref (=> ссылка на ссылку на строку). Второй вызов получает копию ссылки на строку.
Поначалу строка выглядит какой-то особенной. Но строка это просто ссылочный класс, и если вы определите
затем s является ссылкой на строковый класс, который содержит текст «Able»! Еще одно присвоение той же переменной через
не изменяет исходную строку, а просто создает новый экземпляр и позволяет s указать на этот экземпляр!
Вы можете попробовать это с помощью следующего небольшого примера кода:
Что вы ожидаете? То, что вы получите, все еще «Able», потому что вы просто устанавливаете ссылку в s на другой экземпляр, в то время как s2 указывает на исходный экземпляр.
РЕДАКТИРОВАТЬ: строка также является неизменной, что означает, что просто не существует метода или свойства, которое изменяет существующий экземпляр строки (вы можете попытаться найти его в документации, но вы не найдете ничего :-)). Все методы работы со строками возвращают новый экземпляр строки! (Вот почему вы часто получаете лучшую производительность при использовании класса StringBuilder)
источник
ref означает, что значение в параметре ref уже установлено, метод может прочитать и изменить его. Использование ключевого слова ref - это то же самое, что сказать, что вызывающая сторона отвечает за инициализацию значения параметра.
out сообщает компилятору, что за инициализацию объекта отвечает функция, которую функция должна назначить параметру out. Не разрешается оставлять это без назначения.
источник
Out: оператор return может использоваться для возврата только одного значения из функции. Однако, используя выходные параметры, вы можете вернуть два значения из функции. Выходные параметры аналогичны ссылочным параметрам, за исключением того, что они передают данные из метода, а не в него.
Следующий пример иллюстрирует это:
ref: Ссылочный параметр - это ссылка на ячейку памяти переменной. Когда вы передаете параметры по ссылке, в отличие от значений параметров, для этих параметров не создается новое место хранения. Эталонные параметры представляют ту же область памяти, что и фактические параметры, которые передаются в метод.
В C # вы объявляете ссылочные параметры, используя ключевое слово ref. Следующий пример демонстрирует это:
источник
ref и out работают так же, как передача по ссылкам и передача по указателям, как в C ++.
Для ref аргумент должен быть объявлен и инициализирован.
Для, аргумент должен быть объявлен, но может или не может быть инициализирован
источник
out double Half_nbr
.Время создания:
(1) Создаем вызывающий метод
Main()
(2) он создает объект List (который является объектом ссылочного типа) и сохраняет его в переменной
myList
.Во время выполнения:
(3) Среда выполнения выделяет память в стеке в # 00, достаточно широкой для хранения адреса (# 00 =
myList
, поскольку имена переменных на самом деле являются просто псевдонимами для областей памяти)(4) Runtime создает список объектов в куче в ячейке памяти #FF (все эти адреса, например, sakes)
(5) Среда выполнения затем сохранит начальный адрес #FF объекта в # 00 (или в словах сохраняет ссылку на объект List в указателе
myList
)Вернуться к времени разработки:
(6) Затем мы передаем объект List в качестве аргумента
myParamList
вызываемому методуmodifyMyList
и назначаем ему новый объект List.Во время выполнения:
(7) Runtime запускает процедуру вызова для вызываемого метода и, как часть его, проверяет тип параметров.
(8) После нахождения ссылочного типа он выделяет память в стеке в # 04 для наложения псевдонима переменной параметра
myParamList
.(9) Затем он также сохраняет в нем значение #FF.
(10) Runtime создает объект списка в куче в ячейке памяти # 004 и заменяет #FF в # 04 этим значением (или разыменовывает исходный объект List и указывает на новый объект List в этом методе)
Адрес в # 00 не изменяется и сохраняет ссылку на #FF (или исходный
myList
указатель не нарушается).Ссылок Ключевое слово является директива компилятора , чтобы пропустить генерацию кода во время выполнения для (8) и (9) , что означает , что не будет никакого распределения кучи для параметров метода. Он будет использовать оригинальный указатель # 00 для работы с объектом в #FF. Если исходный указатель не инициализирован, среда выполнения остановится с жалобой на невозможность продолжения, поскольку переменная не инициализирована
Из ключевых слов является директива компилятора , который в значительной степени так же , как исх с небольшим изменением в (9) и (10). Компилятор ожидает, что аргумент не будет инициализирован, и продолжит с (8), (4) и (5), чтобы создать объект в куче и сохранить его начальный адрес в переменной аргумента. Неинициализированная ошибка не будет выдана, а все предыдущие сохраненные ссылки будут потеряны.
источник
Кроме того, что вы можете переназначить чужую переменную другому экземпляру класса, вернуть несколько значений и т. Д., Используя
ref
илиout
позволяя кому-либо узнать, что вам нужно от них и что вы намерены делать с предоставленной ими переменной.Вам не нужно
ref
, илиout
если все , что вы собираетесь сделать , это изменить вещи внутри вMyClass
случае , который передается в качестве аргументаsomeClass
.someClass.Message = "Hello World"
того, используете ли выref
,out
или ничегоsomeClass = new MyClass()
внутриmyFunction(someClass)
заменяет объект, видимый толькоsomeClass
в области действияmyFunction
метода. Вызывающий метод все еще знает об оригинальномMyClass
экземпляре, который он создал и передал вашему методуВам нужно
ref
илиout
если вы планируете поменятьsomeClass
объект out для совершенно нового объекта и хотите, чтобы вызывающий метод увидел ваши измененияsomeClass = new MyClass()
внутриmyFunction(out someClass)
изменяет объект, видимый методом, который вызвалmyFunction
Другие программисты существуют
И они хотят знать, что вы собираетесь делать со своими данными. Представьте, что вы пишете библиотеку, которая будет использоваться миллионами разработчиков. Вы хотите, чтобы они знали, что вы собираетесь делать со своими переменными, когда они вызывают ваши методы
Использование
ref
делает утверждение «Передайте переменную, назначенную некоторому значению, когда вы вызываете мой метод. Имейте в виду, что я могу полностью изменить его на что-то другое в ходе моего метода. Не ожидайте, что ваша переменная будет указывать на старый объект». когда я уже закончил"Использование
out
делает утверждение «Передать переменную-заполнитель моему методу. Неважно, имеет ли оно значение или нет; компилятор заставит меня присвоить ему новое значение. Я абсолютно гарантирую, что объект, на который указывает ваш переменная, прежде чем вы вызвали мой метод, будет отличаться к тому времени, как я закончуКстати, в C # 7.2 тоже есть
in
модификаторИ это не позволяет методу поменять местами переданный экземпляр для другого экземпляра. Подумайте об этом, как если бы вы сказали тем миллионам разработчиков: «Передайте мне исходную ссылку на переменную, и я обещаю не менять ваши тщательно созданные данные на что-то другое».
in
имеет некоторые особенности, и в некоторых случаях, например, когда может потребоваться неявное преобразование, чтобы сделать ваш шорт совместимым сin int
компилятором, он временно сделает int, расширит ваш шорт, передаст его по ссылке и завершит работу. Это может быть сделано, потому что вы заявили, что не будете с этим связываться.Microsoft сделала это с помощью
.TryParse
методов числовых типов:Отметив этот параметр, который
out
они здесь активно заявляют, «мы определенно собираемся заменить ваше кропотливое значение 98234957 на что-то другое»Конечно, они должны это делать, например, при синтаксическом анализе типов значений, потому что если бы методу parse не было разрешено поменять тип значения на что-то другое, это не сработало бы очень хорошо ... Но представьте, что в некоторых есть какой-то фиктивный метод библиотека, которую вы создаете:
Вы можете видеть, что это
out
, и, таким образом, вы можете знать, что, если вы часами работаете с цифрами, создавая идеальный SomeClass:Ну, это была пустая трата времени, потратить все эти часы, чтобы сделать этот идеальный урок. Это определенно будет отброшено и заменено PoorlyNamedMethod
источник
Для тех, кто ищет краткий ответ.
источник
Чтобы проиллюстрировать множество превосходных объяснений, я разработал следующее консольное приложение:
AppendWorld
: КопияStringList
имениLiStri
передана. В начале метода эта копия ссылается на исходный список и, следовательно, может использоваться для изменения этого списка. ПозжеLiStri
ссылается на другойList<string>
объект внутри метода, который не влияет на исходный список.HalloWelt
:LiStriRef
это псевдоним уже инициализированногоListStringRef
. ПереданныйList<string>
объект используется для инициализации нового, поэтомуref
был необходим.CiaoMondo
:LiStriOut
является псевдонимомListStringOut
и должен быть инициализирован.Таким образом, если метод просто изменяет объект, на который ссылается переданная переменная, компилятор не разрешит вам его использовать,
out
и вам не следует его использовать,ref
потому что это может сбить с толку не компилятор, а читатель кода. Если метод заставит переданный аргумент ссылаться на другой объект, используйте егоref
для уже инициализированного объекта иout
для методов, которые должны инициализировать новый объект для переданного аргумента. Кроме того,ref
иout
ведите себя так же.источник
Они практически одинаковы - единственное отличие состоит в том, что переменная, передаваемая в качестве параметра out, не требует инициализации, а метод, использующий параметр ref, должен установить его в какое-то значение.
Параметры ref предназначены для данных, которые могут быть изменены, а параметры out - для данных, которые являются дополнительным выходом для функции (например, int.TryParse), которая уже использует возвращаемое значение для чего-либо.
источник
Ниже я показал пример использования как Ref, так и out . Теперь вы все будете очищены от реф и вне.
В приведенном ниже примере, когда я комментирую // myRefObj = new myClass {Name = "ref outside named !!"}; line, получит сообщение об ошибке «Использование неназначенной локальной переменной myRefObj» , но в out такой ошибки нет .
Где использовать Ref : когда мы вызываем процедуру с параметром in, и этот же параметр будет использоваться для хранения выходных данных этого процесса.
Где использовать Out: когда мы вызываем процедуру без параметра in и тот же параметр будет использоваться для возврата значения из этого процесса. Также обратите внимание на вывод
источник
Вы можете проверить этот код, он опишет вам его полное отличие, когда вы используете «ref», это означает, что вы уже инициализируете эту int / string
но когда вы используете "out", он работает в обоих условиях, когда вы инициализируете int / string или нет, но вы должны инициализировать int / string в этой функции
источник
Ref: ключевое слово ref используется для передачи аргумента в качестве ссылки. Это означает, что когда значение этого параметра изменяется в методе, оно отражается в вызывающем методе. Аргумент, который передается с использованием ключевого слова ref, должен быть инициализирован в вызывающем методе, прежде чем он будет передан вызываемому методу.
Out: ключевое слово out также используется для передачи аргумента, такого как ключевое слово ref, но аргумент может быть передан без присвоения ему какого-либо значения. Аргумент, который передается с использованием ключевого слова out, должен быть инициализирован в вызываемом методе, прежде чем он вернется к вызывающему методу.
Ссылка и выход в методе перегрузки
И ref, и out нельзя использовать одновременно в перегрузке метода. Тем не менее, ref и out обрабатываются по-разному во время выполнения, но они обрабатываются одинаково во время компиляции (CLR не делает различий между ними, пока он создает IL для ref и out).
источник
С точки зрения метода, который получает параметр, разница между
ref
и вout
том, что C # требует, чтобы методы записывали каждыйout
параметр перед возвратом, и не должны ничего делать с таким параметром, кроме передачи его в качествеout
параметра или записи в него. до тех пор, пока он не будет передан в качествеout
параметра другому методу или записан напрямую. Обратите внимание, что некоторые другие языки не предъявляют таких требований; виртуальный или интерфейсный метод, который объявлен в C # сout
параметром, может быть переопределен на другом языке, который не накладывает каких-либо особых ограничений на такие параметры.С точки зрения вызывающей стороны, C # будет во многих случаях предполагать, что при вызове метода с
out
параметром будет передана переданная переменная без предварительного чтения. Это предположение может быть неверным при вызове методов, написанных на других языках. Например:Если
myDictionary
идентифицируетIDictionary<TKey,TValue>
реализацию, написанную на языке, отличном от C #, даже еслиMyStruct s = new MyStruct(myDictionary);
выглядит как присваивание, оно может потенциально остатьсяs
неизменным.Обратите внимание, что конструкторы, написанные на VB.NET, в отличие от конструкторов на C #, не предполагают, будут ли вызванные методы изменять какие-либо
out
параметры, и безоговорочно очищают все поля. Необычное поведение, упомянутое выше, не произойдет с кодом, написанным полностью на VB или полностью на C #, но может произойти, когда код, написанный на C #, вызывает метод, написанный на VB.NET.источник
Если вы хотите передать ваш параметр как ссылку, то вам следует инициализировать его перед передачей параметра в функцию, иначе сам компилятор покажет ошибку. Но в случае использования параметра out вам не нужно инициализировать параметр объекта перед передачей его в Метод. Вы можете инициализировать объект в самом вызывающем методе.
источник
Помните, что над эталонным параметром, передаваемым внутри функции, непосредственно воздействуют.
Например,
Это будет писать собака, а не кот. Следовательно, вы должны напрямую работать с некоторым объектом.
источник
Возможно, я не очень хорош в этом, но, конечно, строки (даже если они технически являются ссылочными типами и живут в куче) передаются по значению, а не по ссылке?
Вот почему вам нужен ref, если вы хотите, чтобы изменения существовали вне области действия функции, делающей их, иначе вы не передадите ссылку.
Насколько я знаю, вам нужен только ref для структур / типов значений и самой строки, так как строка является ссылочным типом, который притворяется, что он есть, но не является типом значения.
Я могу быть совершенно не прав, но я новичок.
источник
Capitalize()
, который изменил бы содержимое строки на заглавные буквы. Если вы затем замените свою строкуa = "testing";
наa.Capitalize();
, то ваш вывод будет "HELLO", а не "Hello". Одним из преимуществ неизменяемых типов является то, что вы можете передавать ссылки и не беспокоиться о том, что другой код изменит значение.