Когда использовать ref и когда это не нужно в С#

У меня есть объект, который является моим в состоянии памяти программы, а также имеет некоторые другие рабочие функции, которые я передаю объекту для изменения состояния. Я передал его, обратившись к рабочим функциям. Однако я столкнулся со следующей функцией.

byte[] received_s = new byte[2048];
IPEndPoint tmpIpEndPoint = new IPEndPoint(IPAddress.Any, UdpPort_msg);
EndPoint remoteEP = (tmpIpEndPoint);

int sz = soUdp_msg.ReceiveFrom(received_s, ref remoteEP); 

Это меня смущает, потому что как received_s, так и remoteEP возвращают материал из функции. Почему remoteEP нужен ref и received_s не?

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

Изменить: Похоже, что объекты на С# являются указателями на объект под капотом. Поэтому, когда вы передаете объект функции, вы можете изменить содержимое объекта через указатель, и единственное, что передается функции, - это указатель на объект, поэтому сам объект не копируется. Вы используете ref или out, если хотите, чтобы вы могли отключить или создать новый объект в функции, которая похожа на двойной указатель.

Ответ 1

Короткий ответ: прочитайте статью о передаче аргументов.

Длинный ответ: когда параметр ссылочного типа передается по значению, передается только эта ссылка, а не копия объекта. Это похоже на передачу указателя (по значению) в C или С++. Имена самого параметра не будут видны вызывающей стороне, но изменения объекта, на которые указывает эта ссылка, будут видны.

Когда параметр (любого вида) передается по ссылке, это означает, что любые изменения в параметре видны вызывающим - изменения в параметре являются изменениями в переменной.

В статье объясняется все это более подробно, конечно:)

Полезный ответ: вам почти никогда не нужно использовать ref/out. Это в основном способ получить другое возвращаемое значение и обычно следует избегать именно потому, что это означает, что метод, вероятно, пытается сделать слишком много. Это не всегда случай (TryParse и т.д. - канонические примеры разумного использования out), но использование ref/out должно быть относительной редкостью.

Ответ 2

Подумайте о некорректном параметре как указателе и параметре ref как двойной указатель. Это помогло мне больше всего.

Вы почти никогда не должны передавать значения по ссылке. Я подозреваю, что если это не касается проблем взаимодействия, команда .Net никогда бы не включила его в исходную спецификацию. Способ OO для решения большинства проблем, решаемых параметрами ref, заключается в следующем:

Для нескольких возвращаемых значений

  • Создайте структуры, представляющие множественные возвращаемые значения

Для примитивов, которые изменяются в методе в результате вызова метода (метод имеет побочные эффекты для примитивных параметров)

  • Реализовать метод в объекте как метод экземпляра и управлять состоянием объекта (а не параметрами) как часть вызова метода
  • Используйте решение с множественным возвратным значением и объедините возвращаемые значения в ваше состояние.
  • Создайте объект, который содержит состояние, которое можно манипулировать с помощью метода, и передать этот объект в качестве параметра, а не сами примитивы.

Ответ 3

Возможно, вы могли бы написать целое приложение С# и никогда не передавать никакие объекты/структуры по ссылке.

У меня был профессор, который сказал мне это:

Единственное место, где вы будете использовать refs, - это то, где вы:

  • Хотите передать большой объект (т.е. объекты /struct объекты/структуры внутри него на несколько уровней), и копирование будет быть дорогим и
  • Вы вызываете Framework, Windows API или другой API, который требует он.

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

Я согласен с его советом, и за пять с лишним лет со времени учебы мне никогда не приходилось заходить за рамки Framework или Windows API.

Ответ 4

Так как received_s - это массив, вы передаете указатель на этот массив. Функция управляет этими существующими данными на месте, не изменяя основное местоположение или указатель. Ключевое слово ref означает, что вы передаете фактический указатель на местоположение и обновляете этот указатель во внешней функции, поэтому значение во внешней функции изменится.

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

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

Ответ 5

Подумайте о том, что ссылка означает, что вы передаете указатель по ссылке. Не использовать ref означает, что вы передаете указатель по значению.

Еще лучше, проигнорируйте то, что я только что сказал (это, вероятно, вводит в заблуждение, особенно с типами значений) и читайте Эта страница MSDN.

Ответ 6

Я понимаю, что все объекты, полученные из класса Object, передаются как указатели, тогда как обычные типы (int, struct) не передаются как указатели и требуют ref. Я не уверен в строке (это в конечном итоге происходит из класса Object?)

Ответ 7

Первое правило нуля, примитивы передаются по значению (стек) и Non-Primitive по ссылке (Heap) в контексте вовлеченных TYPES.

Принятые параметры передаются по умолчанию. Хороший пост, который объясняет вещи в деталях. http://yoda.arachsys.com/csharp/parameters.html

Student myStudent = new Student {Name="A",RollNo=1};

ChangeName(myStudent);

static void ChangeName(Student s1)
{
  s1.Name = "Z"; // myStudent.Name will also change from A to Z
                // {AS s1 and myStudent both refers to same Heap(Memory)
                //Student being the non-Primitive type
}

ChangeNameVersion2(ref myStudent);
static void ChangeNameVersion2(ref Student s1)
{
  s1.Name = "Z"; // Not any difference {same as **ChangeName**}
}

static void ChangeNameVersion3(ref Student s1)
{
    s1 = new Student{Name="Champ"};

    // reference(myStudent) will also point toward this new Object having new memory
    // previous mystudent memory will be released as it is not pointed by any object
}

Можно сказать (с предупреждением) Непримитивные типы - это не что иное, как Указатели И когда мы передаем их по ref, мы можем сказать, что мы передаем Double Pointer