Лучшая практика: ByRef или ByVal? в .Net - PullRequest
18 голосов
/ 14 ноября 2008

Что следует учитывать при выборе между ByRef и ByVal.

Я понимаю разницу между этими двумя понятиями, но не до конца понимаю, экономит ли ByRef ресурсы или нам даже нужно беспокоиться об этом в среде .Net.

Как вы выбираете между двумя, если функциональность не имеет значения в ситуации?

Ответы [ 10 ]

34 голосов
/ 14 ноября 2008

Вокруг этого много дезинформации. Главное, чтобы вы понимали разницу между типами значений и ссылочными типами и разницу между передачей по значению и передачей по ссылке .

Вы почти всегда хотите передать по значению. Передача по ссылке почти всегда означает «я хочу вернуть более одного результата, а не просто путем добавления элементов в список, который передается». Классическим примером метода, использующего передачу по ссылке, является Int32.TryParse , где возвращаемое значение является успехом / неудачей, а проанализированное значение «возвращается» параметром out.

15 голосов
/ 14 ноября 2008

Значение по умолчанию byValue для ВСЕХ типов, но важно понимать, что означают эти две опции для «ссылочного типа» (класса), а не для типа значения. (структуры).

Для ссылочного типа, если вы объявляете переменную ссылочного типа в методе, эта переменная является ячейкой памяти в стековом кадре метода. Это не в куче. Когда вы инициализируете эту переменную (используя new или factory, что угодно), вы создали реальный объект в куче, и адрес этого объекта сохраняется в объявленной ссылочной переменной в фрейме стека ваших методов.

Когда вы передаете ссылочный тип другому методу byVal, вы создаете копию адреса, хранящегося в стеке вызывающих методов, и передаете копию этого значения (адрес указателя) в вызываемый метод, где он сохраняется в новый слот памяти в стеке вызываемых методов. Внутри вызываемого метода новая клонированная переменная указывает непосредственно на тот же объект в куче. Таким образом, используя его можно изменить свойства одного и того же объекта. Но вы не можете изменить, на какой объект кучи указывает исходная ссылочная переменная (в стеке вызывающих методов). Если в вызываемом методе я пишу

  myVar = new object();

Исходная переменная в вызывающем методе не будет изменена, чтобы указывать на новый объект.

Если я передаю ссылочный тип byRef, otoh, я передаю указатель на объявленную переменную в стеке вызывающих методов (который содержит указатель на объект в куче). Следовательно, это указатель на указатель на объект. , Он указывает на область памяти в стеке вызывающих методов, которая указывает на объект в куче.
Так что теперь, если я изменяю значение переменной в вызываемом методе, устанавливая его в новый объект (), как указано выше, поскольку это «ссылка на переменную в вызывающем методе», я фактически изменяю, какой объект переменная в вызывающем методе указывает на. Поэтому после возврата вызванного метода переменная в вызывающем методе больше не будет указывать на тот же исходный объект в куче.

8 голосов
/ 14 ноября 2008

Передача объекта ByVal в .net не делает копию объекта и не потребляет больше ресурсов, чем ByRef. Указатель все еще передается функции. Среда выполнения просто гарантирует, что вы не сможете изменить указатель в своей функции и вернуть для него другое значение. Вы все еще можете вносить изменения в значения в объекте, и вы увидите эти изменения вне функции. Вот почему ByRef используется так редко. Это необходимо только тогда, когда вы хотите, чтобы функция изменила реальный объект, который возвращается; следовательно, выходной параметр.

8 голосов
/ 14 ноября 2008

ByVal должен быть вашим "по умолчанию". Используйте его, если у вас нет особых причин использовать ByRef

5 голосов
/ 14 ноября 2008

Используйте «ByRef», только если параметр является «выходным» параметром. В противном случае используйте «ByVal». Использование «ByRef» для параметров, которые явно не должны возвращать значения, опасно и может легко генерировать ошибки.

3 голосов
/ 07 июля 2011

Я бы сказал, что ByRef никогда не следует использовать - это плохая практика. Я бы применил это даже к своему типичному случаю использования - разрешить функции возвращать несколько значений (через параметры ByRef). Было бы лучше, если бы функция возвращала структурированный ответ, включающий эти множественные возвращаемые значения. Это более ясно и более очевидно, если функция возвращает значения только через оператор return.

0 голосов
/ 17 марта 2016

Много путаницы, я постараюсь упростить. У вас есть 4 варианта:

  1. Передать тип значения byVal
  2. Передать тип значения byRef
  3. Передайте объект byVal
  4. Передать объект byRef

Некоторые люди говорят, что вы не должны никогда использовать byRef. Хотя они технически верны, одно можно сказать наверняка. Вы должны НИКОГДА не использовать слово никогда . Если вы разрабатываете систему с нуля, тогда byRef следует избегать любой ценой. Использование этого выставляет недостаток дизайна. Однако работа над существующей системой может не обеспечить такой гибкости, чтобы реализовать хороший дизайн. Иногда вы должны делать выводы, то есть, используя byRef. Например, если вы можете сделать исправление за 2 дня, используя byRef, тогда предпочтительнее заново изобретать колесо и потратить неделю на то же исправление, чтобы просто не использовать byRef.

Резюме:

  1. Использование byVal для типа значения: передает значение в функцию. Это предпочтительный способ функции дизайна.
  2. Использование byRef для типа значения: полезно для возврата более одного значения из функции. если ты создаем функцию, которая должна возвращать более одного значения в существующей системе это может быть лучше, чем создание объекта (и установка свойств, и удаление) просто для одной функции.
  3. Использование byVal для объекта: передает указатель объекта на функцию. Функция может изменить объект.
  4. Использование byRef для объекта: передает указатель на указатель объекта на функцию. Позволяет изменение объекта, на который указывает вызывающая сторона. Это может вызвать некоторые Трудно найти ошибки, и я не могу придумать какой-либо веской причины для его использования. Не означает, что нет, но если есть, их мало и далеко между.
0 голосов
/ 02 октября 2015
Sub last_column_process()
Dim last_column As Integer

last_column = 234
MsgBox last_column

trying_byref x:=last_column
MsgBox last_column

trying_byval v:=last_column
MsgBox last_column

End Sub

Sub trying_byref(ByRef x)
x = 345
End Sub

Sub trying_byval(ByRef v)
v = 555
End Sub
0 голосов
/ 22 марта 2015

Согласно Microsoft, выбор ByVal или ByRef может повлиять на производительность для достаточно больших значений (см. Передача аргументов по значению и по ссылке (Visual Basic) ):

Производительность. Хотя механизм прохождения может повлиять на производительность вашего кода, разница, как правило, незначительна. Одно исключение этому типу значения передается ByVal. В этом случае Visual Basic копирует все содержимое данных аргумента. Следовательно, для большое значение типа, такого как структура, может быть более эффективным для передачи это ByRef.

[выделение добавлено].

0 голосов
/ 03 декабря 2008

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

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

...