C # передача параметров по ссылке - PullRequest
2 голосов
/ 11 октября 2010

У меня есть фрагмент кода ниже, который ставит префикс строки к началу каждого члена массива строк. то есть. ["a", "b", "c"] с префиксом "z" становится ["za", "zb", "zc"].

private string[] Prefix(string[] a, string b) {
   for(int i = 0;i < a.Length;i++) {
     a[i] = b + a[i];
   }
  return a;
}

Функция работает нормально (хотя, если есть лучший способ сделать это, я рад это слышать), но у меня возникают проблемы при передаче параметров.

string[] s1 = new string[] {"a","b"};
string[] s2 = Prefix(s1,"z");

Теперь, насколько я могу судить, я передаю s1 по значению. Но когда функция Prefix завершена, s2 и s1 имеют одинаковое значение ["za," zb "], или s1 был передан по ссылке. Я был уверен, что вам пришлось явно объявить это поведение в c #, и я очень запутался .

Ответы [ 9 ]

5 голосов
/ 11 октября 2010

Как уже говорили, ссылка передается по значению.Это означает, что ваша ссылка s1 скопирована в a, но они все еще ссылаются на один и тот же объект в памяти.Что бы я сделал, чтобы исправить ваш код, напишите его так:

private IEnumerable<string> Prefix(IEnumerable<string> a, string b) {
   return a.Select(s => b + s);
}

.

string[] s1 = new string[] {"a","b"};
string[] s2 = Prefix(s1,"z").ToArray();

Это не только исправит вашу проблему, но и позволит вам работать со списками и другимиколлекции строк в дополнение к простым массивам.

3 голосов
/ 11 октября 2010

C #, как и Java до него, передает все по умолчанию.

Однако для ссылочных типов это значение является ссылкой. Обратите внимание, что string и array являются ссылочными типами.

2 голосов
/ 11 октября 2010

Вот лучший способ сделать это:

private string[] Prefix(string[] a, string b) {
  return a.Select(s => b + s).ToArray();
}

или даже:

private IEnumerable<string> Prefix(IEnumerable<string> a, string b) {
  return a.Select(s => b + s);
}
1 голос
/ 11 октября 2010

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

1 голос
/ 11 октября 2010

На самом деле, ссылка на s1 была передана по значению Prefix(). Хотя теперь у вас есть две разные ссылки, и s1, и s2 все еще ссылаются на один и тот же строковый массив, поскольку массивы являются ссылочными типами в C #.

1 голос
/ 11 октября 2010

A ссылка на массив string передается значением . Следовательно, исходная ссылка на массив в вызывающем методе не может быть изменена, а это означает, что a = new string[10] в методе Prefix не окажет влияния на ссылку s1 в вызывающем методе. Но сам массив является изменяемым, и повторная ссылка на массив того же способна вносить в него изменения таким образом, чтобы это было видно любой другой ссылке на тот же массив.

1 голос
/ 11 октября 2010

Значение объекта передается.Для «ссылочных» объектов это значение ссылки. Клонирование / копирование / дублирование базовых данных не производится.

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

Альтернативно, вы можете сначала клонировать входной массив (который также создает новый объект).Использование клона (который является мелкой копией) в этом случае вполне допустимо, поскольку внутренние члены (строки) сами по себе неизменны - даже если они являются ссылочными типами, значение базового объекта нельзя изменить после создания.Для вложенных изменяемых типов может потребоваться больше внимания.

Вот два метода, которые можно использовать для создания поверхностной копии:

string[] B = (string[])A.Clone();

string[] B = (new List<string>(A)).ToArray();

Это не включено.

Лично я бы использовал LINQ.Вот тизер:

return a.Select(x => b + x).ToArray();
1 голос
/ 11 октября 2010

строки неизменны

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

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

в c # все ссылочные типы по умолчанию передаются по ссылке, т. Е. Классы помещаются в кучу, а не в значения.

1 голос
/ 11 октября 2010

Вы передаете ссылку на s1 по значению.Другими словами, ваш параметр a (в области действия функции Prefix) и переменная s1 являются ссылками на один и тот же массив.

...