Проблема типа значения и ссылочного типа - PullRequest
3 голосов
/ 20 мая 2011

Привет. Я пытаюсь сделать простой обмен двумя объектами. Мой код:

void Main()
{
  object First = 5;
  object Second = 10;

  Swap(First, Second);
  //If I display results it displays as 
  //Value of First as 5 and Second as 10
}

private static void Swap(object First, object Second)
{
  object temp = First;
  First = Second;
  Second = temp;
}

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

Ответы [ 7 ]

6 голосов
/ 20 мая 2011

Посмотрите на это так.

Давайте переименуем параметры в вашем методе подкачки как «x» и «y», потому что делать их такими же, как у локальных, просто сбивает с толку.

У тебя есть яблоко и апельсин. Два объекта.

У вас есть два листа бумаги с надписью «ЯБЛОКО» и «ОРАНЖЕВЫЙ». Это ссылки на объекты.

У вас есть две коробки, одна с надписью Первая и одна с надписью Вторая.

Вы ставите «ЯБЛОКО» в первую и «ОРАНЖЕВЫЙ» во вторую.

Теперь вы получаете еще два поля, помеченные x и y.

Вы делаете фотокопию ЯБЛОКА и кладете ее в х. Вы делаете фотокопию ОРАНЖЕВА и кладете ее в y.

Теперь вы меняете содержимое x и y. APPLE и ORANGE по-прежнему являются ссылками, но вы не изменили содержание First и Second. Вы изменили содержимое х и у.

Теперь предположим, что вы добавили ref к объявлениям параметров x и y. Теперь это означает:

У вас есть две коробки, одна с надписью Первая и одна с надписью Вторая.

Вы ставите «ЯБЛОКО» на первом месте, а «ОРАНЖЕВЫЙ» на втором.

Вы добавляете новый ярлык в поле First; теперь он также может называться х. И Второе также можно назвать y.

Вы меняете содержимое x и y. Поскольку они являются просто псевдонимами для First и Second, содержимое First и Second меняются местами.

Имеет смысл?

5 голосов
/ 20 мая 2011

Здесь есть разные вещи:

  • объекты живут в куче. Забудьте о них сейчас
  • ссылки на объекты - это то, что хранится в First и секунде
  • у вас есть две переменные в Main
  • у вас есть два параметра в Swap

сейчас; важна разница между «ссылочным типом / ссылками» и «передачей по ссылке». Они совершенно не связаны .

в строке:

Swap(First, Second);

вы передаете значения двух переменных в Swap. В этом случае значение из First / Second является ссылкой на упакованный объект .

Next;

private static void Swap(object First, object Second)
{
  object temp = First;
  First = Second;
  Second = temp;
}

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

private static void Swap(ref object First, ref object Second)
{
  object temp = First;
  First = Second;
  Second = temp;
}

Теперь значение First больше не является ссылкой на упакованный объект; это ссылка на ссылку на упакованный объект. У звонящего мы используем:

Swap(ref First,ref Second);

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

Обратите внимание, что я сказал, что вы можете забыть о том факте, что есть объект? Все выше точно так же, если мы использовали:

int x = 1, y = 2;
Swap(ref x, ref y);
void Swap(ref int a, ref int b) {
    var tmp = a; a = b; b = tmp;
}

единственное отличие состоит в том, что значение из x равно 1 и т. Д., А ref x является ссылкой на переменную x. При передаче по ссылке ссылочный тип против значения-типа совершенно не имеет значения; единственная важная вещь - это понимание того, что вы передаете значение переменной по умолчанию, где значение переменной равно 1 (и т. д.), или "ссылка на объект ». В любом случае логика одинакова.

1 голос
/ 20 мая 2011

Вы передаете ссылку на объекты по значению (передача по значению по умолчанию в C #).

Вместо этого вам нужно передать объекты в функцию по ссылке (используя ключевое слово ref).

private static void Swap(ref object first, ref object second)
{
    object temp = first;
    first = second;
    second = temp;
}

void Main()
{
    object first = 5;
    object second = 10;

    Swap(ref first, ref second);
}

Конечно, если вы используете C # 2.0 или новее, вам лучше определить универсальную версию этой функции, которая может принимать параметры любого типа, а не тип object. Например:

private static void Swap<T>(ref T first, ref T second)
{
    T temp;
    temp = first;
    first = second;
    second = temp;
}

Тогда вы можете также объявить переменные с их собственно типом, int.

1 голос
/ 20 мая 2011

Вместо того, чтобы передавать ссылки на объекты по значению, вам нужно передать их по ссылке.Другими словами, вам нужно передать ссылку на ссылку на объект .

. Это достигается с помощью ключевого слова ref.

Например,

private static void Swap(ref object First, ref object Second)
{
  object temp = First;
  First = Second;
  Second = temp;
}
1 голос
/ 20 мая 2011

Вы не передаете его как ссылку, вам нужно явно указать, что вы передаете ссылку:

 Swap(ref First,ref Second);

 private static void Swap(ref object First,ref object Second)
1 голос
/ 20 мая 2011

Объект, на который указывают параметры внутри метода Swap, является ссылкой на объекты First и Second внутри Main, но сами параметры являются локальными для метода Swap.

Итак, если бы вы внутри Swap написали First = 1; Second = 2;, вы бы увидели изменение в объектах внутри Main.Однако вы изменяете только те параметры, которые указаны на Swap , на (назначая их другому объекту), и вообще не меняете объекты.То же самое будет верно, если вы попытаетесь установить для объектов значение null в методе Swap.

0 голосов
/ 20 мая 2011

Вам нужно пройти по ссылке.

Вот некоторая дополнительная информация о передаче по значению: http://www.yoda.arachsys.com/csharp/parameters.html, попробуйте это:

void Main()
{
 object First = 5;
 object Second = 10;

  Swap(ref First, ref Second);
  //If I display results it displays as 
  //Value of First as 5 and Second as 10
}


private static void Swap(ref object First, ref object Second)
{
  object temp = First;
  First = Second;
  Second = temp;
}
...