Лучшая практика: ByRef или ByVal? в .Net

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

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

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

Не уверен, что правильно задал вопрос. Существует ли эквивалент .Net для передачи чего-то ByRef как ReadOnly (как в Java)? Чтобы сэкономить ресурсы?

ctrlShiftBryan 14.11.2008 18:32

Покажите, пожалуйста, синтаксис Java, и мы скажем, можете ли вы сделать то же самое на C#. Имейте в виду, что Java - это чисто передача по значению ...

Jon Skeet 14.11.2008 18:39

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

ctrlShiftBryan 14.11.2008 18:48

Да, но в Java нет ничего подобного ... поэтому я попросил пример Java.

Jon Skeet 14.11.2008 20:15
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
18
4
17 516
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

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

По данным Microsoft: По умолчанию в Visual Basic аргументы передаются по значению. И по этой причине рекомендуется, как правило, опускать ключевое слово ByVal. Подробнее см. этот ответ.
DavidRR 02.01.2014 02:19

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

Sudsy 04.12.2015 22:30

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

Sudsy 04.12.2015 22:40

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

Нет. Используйте «Out», если параметр должен быть «выходным» параметром. Используйте «ByRef», если параметр должен быть параметром «Ссылка».

BrainSlugs83 01.03.2012 11:10
Ответ принят как подходящий

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

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

+1. Передача по ссылке и изменение внутри метода, который возвращает void, - это, на мой взгляд, распространенная «ошибка» - это вводит в заблуждение относительно того, что делает метод.

Kon 14.11.2008 18:04

Многие разработчики по-прежнему склонны использовать этот шаблон - может быть, потому, что их опыт работы с C++ или COM, где ret val всегда были кодами ошибок и этот шаблон был необходим?

Charles Bretana 14.11.2008 18:25

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

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

ctrlShiftBryan 14.11.2008 17:49

Технически это правда, но вводит в заблуждение. При передаче объекта по Val, «объект» не копируется, но адрес объекта копируется и передается вызываемому методу. При передаче byRef адрес объекта НЕ копируется, адрес адресов передается.

Charles Bretana 14.11.2008 18:39

Технически это не так, даже для классов. Но для типов значений это еще более неверно. - Для классов вы передаете КОПИЮ УКАЗАТЕЛЯ классу при передаче byval. Опять же, этот указатель КОПИРОВАННЫЙ. (Следовательно, если вы переназначаете указатель в вызываемой программе, вызывающая сторона не улавливает изменение.) Для типов значений, если вы передаете по значению, поскольку указателя нет, создается ПОЛНАЯ копия; Например, если вы передаете 200-байтовую структуру byval, 200 байтов копируется для каждого вызова - в отличие от byref, копируется только 4 (или 8, в зависимости от архитектуры) байтовый POINTER.

BrainSlugs83 01.03.2012 11:16

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

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

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

  myVar = new object();

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

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

Это, конечно, то, что я узнал в мире Java, но я не уверен, что это так в .Net?

ctrlShiftBryan 14.11.2008 17:48

Я не фанат Java, но слышал, что в Java даже не было типов значений ... Что каждая переменная была ссылочным типом ... Это неправильно?

Charles Bretana 14.11.2008 17:50

Да это не так. В Java есть типы значений. Чего у него нет, так это типов значений, определяемых пользователем.

Jon Skeet 14.11.2008 18:08

ах, значит, вы имеете в виду такие вещи, как int, short, ulong, decimal, char и т.д ... реализуются как типы значений в каждом кадре стека методов, но любой настраиваемый пользовательский тип реализуется как тип ref в куче?

Charles Bretana 14.11.2008 18:22

Да, хотя имейте в виду, что типы значений также попадают в кучу как часть других объектов. См. pobox.com/~skeet/csharp/memory.html

Jon Skeet 14.11.2008 18:39

да, то, что мы называем "полем" в .Net ... переменная, объявленная внутри типа (класса или структуры), который находится вне любого метода, свойства или конструктора ...

Charles Bretana 14.11.2008 18:46

+1, потому что ответ чертовски уместен, учитывая, как был задан вопрос (и правильный) - однако я думаю, что спрашивающий хотел конкретно знать о типах значений в .NET.

BrainSlugs83 01.03.2012 10:27

FYI, в Java, да, есть типы значений для таких вещей, как int, short, double и т. д... есть также типы классов, такие как Integer, Double и т. Д ... в .NET int - это просто псевдоним для Int32, это тип значения с методами и т. д. ... в Java они совсем не совпадают; Integer - это класс, который обертывает int, который дает ему свойства (вы даже передаете int в конструктор). В .NET вы можете пойти дальше и сказать 42.ToString () - попробуйте сделать это на Java.

BrainSlugs83 01.03.2012 10:30

Значение ссылочного типа, находящегося в стеке или куче, не имеет значения. Это деталь реализации, которую можно изменить в любое время. Что важно, так это то, что на тот же объект создается другая ссылка, и она помещается в память (где бы она ни находилась), то есть передается копия REFERENCE. Для структур передается копия ОБЪЕКТА.

jmoreno 13.02.2017 18:23

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

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

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

Ага, совершенно верно. Включение его в .NET было серьезной ерундой.

Konrad Rudolph 07.07.2011 20:50

Я не согласен. Это может быть чрезвычайно полезно для таких вещей, как классический метод обмена: «Swap <T> (ref T a, ref T b) where T: struct {...}» (извиняюсь, если я ошибся в синтаксисе)

BrainSlugs83 01.03.2012 11:22

Как можно использовать Threading.Interlocked.Increment и других подобных участников без ByRef?

supercat 28.06.2012 02:53

Это именно то, что вам нужно, когда это нужно. Однако в большинстве случаев вы этого не делаете. Поэтому следует использовать только тогда, когда вам это особенно нужно.

DarcyThomas 10.10.2012 09:20

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

N.T.C 18.08.2014 15:51

Проектное решение определяет, нужно ли вам использовать ByRef. Я программировал на .NET с самого начала, и я никогда не принимал решения, которое заставило бы меня в этом нуждаться.

Mario 24.09.2014 17:11

Не могли бы вы использовать ByRef при предоставлении параметров для функции, вызывающей базу данных, которая возвращает количество найденных записей? Записи, которые вы получаете обратно, находятся в параметре ByRef.

Luminous 01.10.2014 22:26

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

Performance. Although the passing mechanism can affect the performance of your code, the difference is usually insignificant. One exception to this is a value type passed ByVal. In this case, Visual Basic copies the entire data contents of the argument. Therefore, for a large value type such as a structure, it can be more efficient to pass it ByRef.

[курсив добавлен].

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

Я постараюсь упростить эту путаницу. В основном у вас есть 4 варианта:

  1. Передайте тип значения по Val
  2. Передайте тип значения по ссылке
  3. Передать объект по Val
  4. Передать объект по ссылке

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

Резюме:

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

Другие вопросы по теме