Почему я могу изменить свойство int [] в Struct из метода без указания "ref"? - PullRequest
1 голос
/ 10 апреля 2010

Из метода я могу передать структуру, которая содержит массив целых чисел, и изменить значения в массиве. Я не уверен, что полностью понимаю, почему я могу это сделать. Может кто-нибудь объяснить, почему я могу изменить значения, хранящиеся в int []?

    private void DoIt(){

        SearchInfo a = new SearchInfo();
        a.Index = 1;
        a.Map = new int[] { 1 };

        SearchInfo b = new SearchInfo();
        b.Index = 1;
        b.Map = new int[] { 1 };

        ModifyA(a);
        ModifyB(ref b);

        Debug.Assert(a.Index == 1);
        Debug.Assert(a.Map[0] == 1, "why did this change?");

        Debug.Assert(b.Index == 99);
        Debug.Assert(b.Map[0] == 99);

    }
    void ModifyA(SearchInfo a) {
        a.Index = 99;
        a.Map[0] = 99;
    }
    void ModifyB(ref SearchInfo b) {
        b.Index = 99;
        b.Map[0] = 99;
    }
    struct SearchInfo {
        public int[] Map;
        public int Index;
    }

Ответы [ 5 ]

5 голосов
/ 10 апреля 2010

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

В отличие от языков, подобных C ++, вы не можете объявлять «неизменяемые» массивы в C # - однако вы можете использовать классы, такие как List, которые имеют доступные только для чтения оболочки, чтобы предотвратить изменение коллекции.

2 голосов
/ 10 апреля 2010

Из метода я могу передать структуру, которая содержит массив целых чисел, и изменить значения в массиве. Я не уверен, что полностью понимаю, почему я могу это сделать.

Массив определяется как набор переменных .

Переменные, по определению, могут быть изменены . Вот почему мы называем их «переменными».

Поэтому, когда вы передаете массив, вы можете изменить его содержимое; содержимое массива: переменные .

Почему я могу изменить свойство int [] структуры без указания "ref"?

Помните, как мы уже говорили в другом вопросе, вы используете ref для , чтобы создать псевдоним для переменной . Для этого и используется ref - создание псевдонимов для переменных. (К сожалению, это ключевое слово является запутанным «ref» - ​​возможно, было бы более понятным сделать его «псевдонимом».)

1 голос
/ 10 апреля 2010

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

Когда вы передаете экземпляр SearchInfo методу, как вы знаете, SearchInfo копируется. И копия, естественно, содержит тот же адрес, указывающий на тот же массив.

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

1 голос
/ 10 апреля 2010

С MSDN :

Не возвращать внутренний экземпляр массива. Это позволяет вызывающему коду изменять массив. В следующем примере показано, как массив badChars может быть изменен любым кодом, который обращается к свойству Path, даже если свойство не реализует метод доступа set.

using System;
using System.Collections;

public class ExampleClass
{
   public sealed class Path
   {
      private Path(){}
      private static char[] badChars = {'\"', '<', '>'};
      public static char[] GetInvalidPathChars()
      {
         return badChars;
      }
   }
   public static void Main()
   {
      // The following code displays the elements of the 
      // array as expected.
      foreach(char c in Path.GetInvalidPathChars())
      {
         Console.Write(c);
      }
      Console.WriteLine();

      // The following code sets all the values to A.
      Path.GetInvalidPathChars()[0] = 'A';
      Path.GetInvalidPathChars()[1] = 'A';
      Path.GetInvalidPathChars()[2] = 'A';

      // The following code displays the elements of the array to the
      // console. Note that the values have changed.
      foreach(char c in Path.GetInvalidPathChars())
      {
         Console.Write(c);
      }
   }
}

Вы не можете исправить проблему в предыдущем примере, сделав массив badChars доступным только для чтения (ReadOnly в Visual Basic). Вы можете клонировать массив badChars и вернуть копию, но это существенно влияет на производительность.

0 голосов
/ 10 апреля 2010

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

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

...