Когда использовать ref и когда это не нужно в C#
у меня есть объект, который является моим состоянием памяти программы, а также некоторые другие рабочие функции, которые я передаю объекту для изменения состояния. Я передал его 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, поэтому у меня есть проблема становится указатели из головы.
изменить: Похоже, что объекты в C# являются указателями на объект под капотом. Поэтому, когда вы передаете объект функции, вы можете изменить содержимое объекта через указатель, и единственное, что передается функции, - это указатель на объект, поэтому сам объект не копируется. Вы используете ref или out, если хотите иметь возможность переключаться или создавать новый объект в функции, которая похожа на двойной указатель.
8 ответов
короткий ответ: прочитайте мой статья о передаче аргументов.
длинный ответ: когда параметр ссылочного типа передается по значению, передается только ссылка,не копия объекта. Это похоже на передачу указателя (по значению) в C или c++. Изменения значения самого параметра не будут видны вызывающему объекту, но изменения объекта, на который указывает ссылка будет быть видно.
когда параметр (любого вида) прошло by ссылка, это означает, что любые изменения параметра видны вызывающему абоненту-изменения параметра are изменения переменной.
в статье все это объясняется более подробно, конечно:)
полезный ответ: вам почти никогда не нужно использовать ref/out. Это в основном способ получения другого возвращаемого значения, и его обычно следует избегать именно потому, что это означает, что метод, вероятно, пытается сделать тоже много. Это не всегда так (TryParse
etc являются каноническими примерами разумного использования out
) но использование ref/out должно быть относительной редкостью.
подумайте о параметре non-ref как о указателе, а параметр ref как о двойном указателе. Это помогло мне больше всего.
вы почти никогда не должны передавать значения по ref. Я подозреваю, что если бы не проблемы взаимодействия, команда .Net никогда бы не включила его в исходную спецификацию. Способ решения большинства проблем, которые решают параметры ref, заключается в следующем:
для нескольких возвращаемых значений
- создать структуры, представляют несколько возвращаемых значений
для примитивов, которые изменяются в методе в результате вызова метода (метод имеет побочные эффекты на примитивных параметров)
- реализуйте метод в объекте как метод экземпляра и манипулируйте состоянием объекта (а не параметрами) как частью вызова метода
- используйте решение с несколькими возвращаемыми значениями и объедините возвращаемые значения в свое состояние
- создать объект это содержит состояние, которым можно управлять методом и передавать этот объект в качестве параметра, а не сами примитивы.
вы, вероятно, могли бы написать целое приложение c# и никогда не передавать какие-либо объекты/структуры по ref.
У меня был профессор, который сказал мне это:
единственное место, где вы будете использовать refs, - это то, где вы либо:
- хотите передать большой объект (т. е. объекты / структура имеет объекты / структуры внутри него на несколько уровней) и копирование его быть дорогим и
- вы вызываете фреймворк, Windows API или другой API, который требует он.
Не делай этого только потому, что вы можете. Вы можете получить укус в задницу некоторые неприятных ошибок если вы начинаете изменять значения в param и не внимание.
Я согласен с его советом, и за мои пять с лишним лет со школы у меня никогда не было необходимости в нем за пределами вызова фреймворка или Windows API.
поскольку received_s является массивом, вы передаете указатель на этот массив. Функция управляет существующими данными на месте, не изменяя базовое местоположение или указатель. Ключевое слово ref означает, что вы передаете фактический указатель на местоположение и обновляете этот указатель во внешней функции, поэтому значение во внешней функции изменится.
Е. Г. байтовый массив-это указатель на ту же память до и после, в памяти только что усовершенствованный.
ссылка на конечную точку фактически обновляет указатель на конечную точку во внешней функции до нового экземпляра, созданного внутри функции.
подумайте о ref как о том, что вы передаете указатель по ссылке. Не использовать ref означает, что вы передаете указатель по значению.
еще лучше, игнорировать то, что я только что сказал (Это, наверное, вводит в заблуждение, особенно с типами значений) и читать эта страница MSDN.
Я понимаю, что все объекты, производные от класса Object, передаются как указатели, тогда как обычные типы (int, struct) не передаются как указатели и требуют ref. Я не уверен в string (является ли он в конечном счете производным от класса Object ?)
хотя я согласен с ответом Джона Скита в целом и некоторыми другими ответами, есть вариант использования для использования ref
, а это для ужесточения оптимизации производительности. Во время профилирования производительности было замечено, что установка возвращаемого значения метода имеет незначительные последствия для производительности, тогда как использование ref
в качестве аргумента, при котором возвращаемое значение заполняется в этот параметр, это небольшое узкое место удаляется.
Это действительно только полезно, когда усилия по оптимизации принимаются на экстремальных уровнях, жертвуя читаемостью и, возможно, тестируемостью и ремонтопригодностью для экономии миллисекунд или, возможно, сплит-миллисекунд.
Ground zero во-первых, примитивы передаются по значению(стеку) и не примитивы по ссылке(куче) в контексте задействованных типов.
параметры передаются по значению по умолчанию. Хороший пост, который подробно объясняет вещи. 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, мы можем сказать, что мы проходя Двойной Указатель