Должен ли аргумент передаваться по ссылке в этом примере .net? - PullRequest
2 голосов
/ 20 мая 2010

Я использовал Java, C ++, .Net. (в этой последовательности). Когда меня спрашивали об интервью «цена-качество» или «я-реф», я всегда хорошо справлялся с этим вопросом ... возможно, потому что никто не углублялся в него. Теперь я знаю, что не вижу всей картины.

Я просматривал этот раздел кода, написанного кем-то другим:

XmlDocument doc = new XmlDocument();
AppendX(doc); // Real name of the function is different
AppendY(doc); // ditto

Когда я увидел этот код, я подумал: подождите минуту, не следует ли мне использовать ref перед переменной doc (и соответственно изменить AppendX/Y? Она работает как написано, но заставила меня задаться вопросом на самом деле понять ключевое слово ref в C #.

Когда я думал об этом больше, я вспомнил ранние дни Java (вводный язык колледжа). Мой друг посмотрел какой-то код, который я написал, и у него был ментальный блок - он продолжал спрашивать меня, какие вещи передаются по ссылке, а когда по значению. Мой невежественный ответ был что-то вроде: Чувак, в Java передается только один вид аргументов, и я забыл, какой это :). Охладись, не задумывайся и просто код.

У Java все еще нет ref, не так ли? Тем не менее, Java-хакеры кажутся продуктивными. Как бы то ни было, кодирование на C ++ раскрыло мне все это благодаря ссылочному бизнесу, и теперь я в замешательстве.

Следует ли использовать ref в приведенном выше примере?

Я предполагаю, что когда ref применяется к типам значений: примитивам, перечислениям, структурам (есть ли что-нибудь еще в этом списке?), Это имеет большое значение. И ... применительно к объектам это не так, потому что это все по ссылке. Если бы все было так просто, то почему бы компилятору не ограничивать использование ключевого слова ref подмножеством типов.

Когда дело доходит до объектов, ref служит своего рода комментарием? Ну, я помню, что могут быть проблемы с null, и ref также полезен для инициализации нескольких элементов в методе (так как вы не можете вернуть несколько вещей так же просто, как в Python).

Спасибо.

Ответы [ 10 ]

10 голосов
/ 20 мая 2010

Ты не одинок в замешательстве.

По умолчанию все передается по значению в C #, но в случае ссылочного типа (такого как XmlDocument) это значение является ссылкой.

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

См. Мою статью о передаче параметров для более подробной информации.

4 голосов
/ 20 мая 2010

Метод, который принимает переменную ссылочного типа, переданную ref, может изменить то, на что указывает переменная, - не просто изменить его.

4 голосов
/ 20 мая 2010

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

2 голосов
/ 20 мая 2010

http://www.albahari.com/valuevsreftypes.aspx дает отличное объяснение разницы между ссылочными типами и типами значений в .Net.

2 голосов
/ 20 мая 2010

Это зависит от того, что вы хотите, чтобы AppendX делал. Если он изменяет содержимое объекта, его не нужно передавать по ссылке. Если вы хотите, чтобы AppendX мог изменять, на какой объект указывает переменная «doc», вам необходимо. «doc» - это уже «ссылочный тип», который эквивалентен указателю на объект в c ++.

2 голосов
/ 20 мая 2010

Нет, не используйте ключевое слово ref. Вы хотите использовать содержимое документа, а не изменять сам документ. Смотрите этот пост для дальнейшего объяснения: ref ключевое слово

1 голос
/ 21 мая 2010

Многие люди смущены этим. Вот как я об этом думаю. Я вообще не думаю, что «ссылка» означает «ссылка». Я думаю, что «ref» означает «псевдоним». То есть, когда вы говорите

void M(ref int x) { x = 123; }
void N(int z) { ... }
...
int y = 456;
M(ref y);
N(y);

что означает "ref y", это "пожалуйста, сделайте соответствующий формальный параметр x * псевдоним для переменной y". То есть x и y теперь являются переменными для одного и того же места хранения . Когда вы пишете в x, вы пишете в y, потому что x - это другое имя для y.

Когда вы передаете без ref, как в N (y), вы говорите: «y и z - две разные переменные, так что z начинает свое время жизни с того же содержимого, что и y».

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

Хотелось бы, чтобы мы использовали "псевдоним" вместо "ref"; это было бы намного яснее.

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

Если вы уже программировали на C ++, вы должны быть знакомы с указателями. Ссылка на объект в .NET-коде и Java является указателем под капотом. Это просто не написано явно в синтаксисе, который делает очевидным, что это указатель, вы должны запомнить его. Правило не очень сложное, любая переменная, которая ссылается на объект класса, массива, строки или System.Object, является ссылочным типом, а переменная является указателем. Все остальное является типом значения, а переменная содержит фактическое значение.

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

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

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

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

Вы не одиноки, когда вас смущают и кодируют на c ++ и c #. Я могу понять, почему (не как критика, а как комментарий, потому что это мир, в котором я тоже живу) в c ++, когда вы используете & для аргумента для передачи ссылка, которую вы в основном говорите, это псевдоним, который я буду использовать внутри метода для аргумента, передаваемого методу. Все, что вы сделаете с этим аргументом , будет иметь такой же эффект, как если бы вы использовали саму переменную. так что в коде вы можете сделать: void Foo (MyClass & arg) { arg = новый MyClass (1); }

int x = new MyClass(0);
Foo(x);

или

int x = new MyClass(0);
void Foo()
{
   x = new MyClass(1);
}

в любом случае x теперь равно MyClass (1) (и у вас есть утечка, потому что нет способа добраться до оригинала, но это не моя точка зрения). Я думаю, от вас вопрос, который вы уже знали, но он все равно будет служить цели:)

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

с #

MyClass x = MyClass(0);
void Foo(MyClass arg) //reference being passed by val
{
  arg = new MyClass(1);
}
Foo(x);

x по-прежнему равен MyClass (0)

MyClass x = MyClass(0);
void Foo(ref MyClass arg) //passing by ref
{
  arg = new MyClass(1);
}
Foo(ref x);

x равно MyClass (1)

Таким образом, стандартная передача аргумента в C # отличается от передачи ссылки в C ++, но использование ключевого слова ref (что не приветствуется) дает вам почти такую ​​же механику, как и в c ++ & в C ++ обычно поощряется из-за оптимизации / отсутствия копирования, поскольку, поскольку вы копируете только ссылку в C #, это не имеет значения, и ref следует использовать только тогда, когда вам действительно нужен псевдоним для переменной, передаваемой методу aka, когда вы потенциально необходимо назначить новый экземпляр объекта переменной, а не использовать / изменять состояние объекта

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

Я спорил об этом с некоторыми коллегами и в конечном итоге проиграл; вот как я научился делать все по-своему.

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

Я не полностью согласен с этим, но определенно имеет смысл иметь что-то, что означало бы, что «ссылка изменится».

...