Почему список передается без ссылки на функцию, действующую как переданная с помощью ref?
Если я не ошибаюсь, это поведение странно для меня. Вместо того, чтобы объяснять, я отправлю пример кода ниже и, пожалуйста, скажите мне, почему я получаю выходные данные x, а не y.
private void button1_Click(object sender, EventArgs e)
{
List<int> l = new List<int>() { 1, 2, 3 };
Fuss(l);
MessageBox.Show(l.Count.ToString());
}
private void Fuss(List<int> l)
{
l.Add(4);
l.Add(5);
}
Выход должен, я предполагаю, будет 3. Но я получаю вывод как 5. Я понимаю, что вывод может быть 5, если я это сделаю:
private void button1_Click(object sender, EventArgs e)
{
List<int> l = new List<int>() { 1, 2, 3 };
Fuss(ref l);
MessageBox.Show(l.Count.ToString());
}
private void Fuss(ref List<int> l)
{
l.Add(4);
l.Add(5);
}
Ответы
Ответ 1
Он не действует так, как он прошел по ссылке.
void ChangeMe(List<int> list) {
list = new List<int>();
list.Add(10);
}
void ChangeMeReally(ref List<int> list) {
list = new List<int>();
list.Add(10);
}
Попробуйте. Вы заметили разницу?
Вы можете изменить содержимое списка (или любого ссылочного типа), если вы передадите его без ссылки (потому что, как говорили другие, вы передаете ссылку на объект в куче и, таким образом, меняете одну и ту же "память",).
Однако вы не можете изменить "список" , "список" - это переменная, указывающая на объект типа List. Вы можете изменить только "список" , если вы передадите его по ссылке (чтобы он указывал в другом месте). Вы получаете копию ссылки, которая, если она изменена, может наблюдаться только внутри вашего метода.
Ответ 2
Параметры передаются по значению в С#, если они не помечены модификаторами ref
или out
. Для ссылочных типов это означает, что ссылка передается по значению. Поэтому в Fuss
, l
ссылается на тот же экземпляр List<int>
, что и его вызывающий. Поэтому любые модификации этого экземпляра List<int>
будут отображаться вызывающим.
Теперь, если вы помечаете параметр l
ref
или out
, тогда параметр передается по ссылке. Это означает, что в Fuss
, l
является псевдонимом для места хранения, используемого в качестве параметра для вызова метода. Чтобы быть ясным:
public void Fuss(ref List<int> l)
вызываемый
List<int> list = new List<int> { 1, 2, 3 };
Fuss(list);
Теперь, в Fuss
, l
является псевдонимом для list
. В частности, если вы назначаете новый экземпляр List<int>
на l
, вызывающий объект увидит, что новый экземпляр присваивается переменной list
. В частности, если вы скажете
public void Fuss(ref List<int> l) {
l = new List<int> { 1 };
}
то вызывающий теперь увидит список с одним элементом. Но если вы скажете
public void Fuss(List<int> l) {
l = new List<int> { 1 };
}
и вызов
List<int> list = new List<int> { 1, 2, 3 };
Fuss(list);
то вызывающий объект все равно будет видеть list
как имеющий три элемента.
Ясно?
Ответ 3
Разница между ref и non-ref для ссылочных типов, таких как List, заключается не в том, передаете ли вы ссылку (что всегда бывает), но может ли эта ссылка быть изменена. Попробуйте следующее
private void Fuss(ref List<int> l)
{
l = new List<int> { 4, 5 };
}
и вы увидите, что счетчик равен 2, потому что функция не только управляет исходным списком, но и самой ссылкой.
Ответ 4
Списки уже являются ссылочными типами, поэтому, передавая их методу, вы передаете ссылку. Любые вызовы Add
будут влиять на список в вызывающем абоненте.
Передача List<T>
на ref
ведет себя по существу так же, как передача двойного указателя на этот список. Вот иллюстрация:
using System;
using System.Collections.Generic;
public class Test
{
public static void Main()
{
List<int> l = new List<int>() { 1, 2, 3 };
Fuss(l);
Console.WriteLine(l.Count); // Count will now be 5.
FussNonRef(l);
Console.WriteLine(l.Count); // Count will still be 5 because we
// overwrote the copy of our reference
// in FussNonRef.
FussRef(ref l);
Console.WriteLine(l.Count); // Count will be 2 because we changed
// our reference in FussRef.
}
private static void Fuss(List<int> l)
{
l.Add(4);
l.Add(5);
}
private static void FussNonRef(List<int> l)
{
l = new List<int>();
l.Add(6);
l.Add(7);
}
private static void FussRef(ref List<int> l)
{
l = new List<int>();
l.Add(8);
l.Add(9);
}
}
Ответ 5
ByRef и ByVal применяются только к типам значений, а не к типам ссылок, которые всегда передаются так, как если бы они были "byref".
Если вам нужно модифицировать список незаметно, используйте функцию ".ToList()", и вы получите клон вашего списка.
Имейте в виду, что если ваш список содержит ссылочные типы, ваш "новый" список содержит указатели на те же объекты, что и в вашем исходном списке.
Ответ 6
Переменная, параметр или поле типа "Список" или любой другой ссылочный тип фактически не содержат список (или объект любого другого класса). Вместо этого он будет содержать что-то вроде "Object ID # 29115" (но не такая фактическая строка, конечно, но комбинация бит, что в основном означает это). В другом месте система будет иметь индексированный набор объектов, называемых кучей; если какая-либо переменная типа List содержит "Object ID # 29115", тогда объект # 29115 в куче будет экземпляром List или некоторого типа, полученного из него.
Если MyFoo - это переменная типа List, выражение типа MyFoo.Add( "George" ) на самом деле не изменит MyFoo; вместо этого это означает "Изучить идентификатор объекта, хранящийся в MyFoo, и вызвать метод" Добавить "объекта, хранящегося в нем. Если MyFoo провел" Object ID # 19533 "до выполнения оператора, он будет продолжать делать это позже, но Object У ID # 19533 будет активирован метод Add (возможно, это изменение объекта). И наоборот, оператор типа MyFoo = MyBar заставит MyFoo сохранить тот же идентификатор объекта, что и MyBar, но на самом деле ничего не сделает для объектов в вопрос. Если MyBar провел" Object ID # 59212 "перед оператором, то после утверждения MyFoo также проведет" ObjectId # 59212". Ничего не произойдет с идентификатором объекта # 19533 или с идентификатором объекта # 59212.
Ответ 7
Только примитивные типы, такие как int, double и т.д., передаются по значению.
Сложные типы (например, список) передаются через ссылку (которая является указателем-переходом по значению, если быть точным).