Как код достиг передачи по ссылке? - PullRequest
2 голосов
/ 23 октября 2009

Внутри main я объявил локальный массив int [] (int [] nums). Я не передал это по ссылке. Но когда я печатаю значения локального массива, я получаю квадратное значение каждого элемента. В чем причина?

delegate void tsquare(int[] a);

static void Main()
{
     int[] nums = { 1, 2, 3 };
     tsquare sqr = new tsquare(SomeClass.Square);
     sqr(nums);

     foreach (int intvals in nums)
     {
       Console.WriteLine(intvals);
     }
}


   class SomeClass
   {

     public static void Square(int[] array)
     {
         for (int i = 0; i < array.Length; i++)
         {
             array[i] = array[i] * array[i];
         }
     }

   }

Обновление:

Мои извинения всем. Я подумал, что int [] {Array} является типом значения, и делегат сделал Немного подвоха. Теперь из вашего ответа я понимаю, что Array - это тип Reference.

Ответы [ 7 ]

3 голосов
/ 23 октября 2009

Здесь есть два понятия.

  1. Типы ссылок и типы значений
  2. Передача по значению против передачи по ссылке

Давайте сначала займемся вторым.

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

Например, это:

Int32 x = 10;
SomeMethod(x); // pass by value

Нет никакого способа, которым x будет чем-то иным, чем 10, после того, как вызов вернется в этом случае, поскольку независимо от того, что SomeMethod сделал со своей копией значения, он сделал только со своим собственным значением.

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

Итак, это:

Int32 x = 10;
SomeMethod(ref x); // pass by reference

В этом случае x может содержать другое значение после возврата SomeMethod, чем до вызова.

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

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

Ссылочный тип на самом деле состоит из двух частей. Это ссылка, и это то, на что ссылается ссылка. Подумайте о доме, адрес которого вы знаете. Когда вы пишете адрес на листе бумаги, на самом деле весь дом не помещается на эту бумагу, скорее, у вас есть «ссылка» на этот конкретный дом на листе бумаги.

Ссылочный тип в .NET - это то же самое. Где-то в памяти есть объект, представляющий собой набор значений, сгруппированных вместе. Адрес этого объекта вы храните в переменной. Эта переменная объявляется как тип, который является ссылочным типом, который разрешает эту сделку из двух частей.

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

Редактировать : В отношении вопроса массив является ссылочным типом. Это означает, что ваша переменная содержит только адрес фактического массива, и этот объект массива находится где-то еще в памяти.

Однако тип значения - это одно, все значение является частью «типа значения», и когда вы его копируете, вы делаете отдельные копии

Вот пример типов значений:

struct SomeType
{
    public Int32 Value;
}

SomeType x = new SomeType;
x.Value = 10;
SomeType y = x; // value type, so y is now a copy of x
y.Value = 20; // x.Value is still 10

Однако для ссылочного типа вы не делаете копию объекта , на который он ссылается, только ссылка на него. Думайте об этом как о копировании адреса этого дома на второй лист бумаги. У вас все еще есть только один дом.

Итак, просто изменив тип SomeType на тип ссылки (изменив struct на class):

class SomeType
{
    public Int32 Value;
}

SomeType x = new SomeType;
x.Value = 10;
SomeType y = x; // reference type, so y now refers to the same object x refers to
y.Value = 20; // now x.Value is also 20, since x and y refer to the same object

А теперь напоследок; передача ссылочного типа по значению.

Возьми этот метод:

public void Test(SomeType t)
{
    t.Value = 25;
}

Учитывая нашу классовую версию SomeType выше, у нас есть метод, который принимает параметр ссылочного типа, но принимает его как передаваемый по значению.

Это означает, что Test не может изменить t, чтобы ссылаться на другой объект в целом, и сделать это изменение обратно в вызывающий код. Думайте об этом как о том, чтобы позвонить другу и дать ему адрес, который у вас есть на вашем листе бумаги. Независимо от того, что твой друг делает с этим домом, адрес, который у тебя есть на бумаге, не изменится.

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

Если вы измените метод для передачи ссылочного типа по ссылке, этот метод не только свободен для переупорядочения содержимого объекта, на который ссылаются, но и метод также может заменить объект совершенно новым объектом, и иметь это изменение отражать обратно в вызывающий код. По сути, ваш друг может сказать вам: «С этого момента, используйте этот новый адрес, который я прочитаю вам, вместо старого, и вообще забудьте старый».

2 голосов
/ 23 октября 2009

Большинство других ответов верны, но я полагаю, что терминология сбивает с толку и требует объяснения. По умолчанию вы можете сказать, что все параметры в C # передаются по значению, то есть содержимое переменной копируется в переменную метода. Это интуитивно понятно с переменными типов значений, но хитрость заключается в том, чтобы помнить, что переменные, являющиеся ссылочными типами (включая массивы), фактически являются указателями. Место в памяти, которое содержит указатель , копируется в метод при его передаче.

Когда вы применяете модификатор ref , метод получает фактическую переменную от вызывающей стороны. По большей части поведение такое же, но учтите следующее:

public void DoesNothing(int[] nums)
{
  nums = new []{1, 2, 3, 4};
}

В DoesNothing мы создаем новый массив int и присваиваем ему nums. Когда метод завершается, присваиватель не видит назначения, потому что метод манипулировал копией ссылки (указателя), которая была передана.

public void DoesSomething(ref int[] nums)
{
  nums = new []{1, 2, 3, 4};
}

С помощью ключевого слова ref метод может по существу обратиться и повлиять на саму исходную переменную из вызывающей стороны.

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

2 голосов
/ 23 октября 2009

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

Читать:

Типы ссылок

Типы значений

2 голосов
/ 23 октября 2009

В C # все параметры по умолчанию передаются по значению. В C # есть два типа типов, а именно типы значений и ссылки.

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

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

Для типов значений это более просто, но это та же концепция. Помните, что int[] является ссылочным типом (как и все массивы).

Рассмотрим различия в этих функциях при передаче некоторого массива целых чисел:

     public static void Square1(int[] array)
     {
         for (int i = 0; i < array.Length; i++)
         {
             array[i] = array[i] * array[i];
         }
     }

     public static void Square2(int[] array)
     {
         array = {10, 20, 30};
         for (int i = 0; i < array.Length; i++)
         {
             array[i] = array[i] * array[i];
         }
     }

     public static void Square3(ref int[] array)
     {
         array = {10, 20, 30};
         for (int i = 0; i < array.Length; i++)
         {
             array[i] = array[i] * array[i];
         }
     }
1 голос
/ 23 октября 2009

Вы не передаете это по ссылке. Массив передается по значению, но массивы в .NET являются ссылочными типами, поэтому вы передаете ссылку на массив, поэтому вы видите значения в квадрате.

0 голосов
/ 23 октября 2009

Прочитайте следующий вопрос SO - он объясняет разницу между передачей по значению и передачей по ссылке. В принятом ответе есть ссылка на хорошую статью на эту тему, которая должна помочь вам понять разницу.

что отличается между передачей по значению и передачей по ссылке с использованием C #

0 голосов
/ 23 октября 2009

Массивы являются объектами и передаются по ссылке. Интты являются структурами и передаются по значению (если только вы не используете ключевое слово ref в сигнатуре вашего метода согласно разборчивому парню в комментариях) (который был прав) (но разборчиво).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...