Действительно ли аргумент передается по значению?

Я читаю книгу Джона Скита "C# in Depth", На странице 74 он сказал, что все предполагают, что аргументы, передаваемые в функцию по ссылке, в то же время передают по значению, и в качестве примера он показывает этот код, который должен доказать StringBuilder в вызывающем коде, не был изменен. Между тем внутри нашей функции экземпляр StringBuilder изменился.

private static void SayHello(StringBuilder s)
 {
   s.AppendLine("Hello");
 }

Но мой эксперимент показал, что объект StringBuilder получил изменения - мы увидим "Hello" в консоли. Что здесь не так? Или что не так с моим пониманием этого примера?

       private static void Main(string[] args)
        {
           var s  = new StringBuilder();
            Console.WriteLine(s.ToString());
            SayHello(s);
            Console.WriteLine(s.ToString());
            Console.ReadLine();
        }

        private static void SayHello(StringBuilder s)
        {
            s.AppendLine("Hello");
        }

2 ответа

Решение

После прочтения страницы он говорит следующее.

Теперь запомните, что значением переменной ссылочного типа является ссылка, а не сам объект. Вы можете изменить содержимое объекта, на который ссылается параметр, без передачи самого параметра по ссылке.

    public static void Main()
    {
        var s = new StringBuilder();
        Console.WriteLine(s.ToString());
        SayHello(s);
        Console.WriteLine(s.ToString());
        Console.ReadLine();
    }

    private static void SayHello(StringBuilder s)
    {
        s.AppendLine("Hello");
        s = null;
    }

    //output will be Hello

Когда этот метод вызывается, значение параметра (ссылка на StringBuilder) передается по значению. Если бы я изменил значение переменной построителя внутри метода - например, с помощью оператора builder = null; - это изменение не было бы замечено вызывающей стороной, вопреки мифу.

Он так много объясняет, что вы не можете уничтожить объект StringBuilder в SayHello вы можете просто изменить содержимое этого объекта. Я тоже этого не знал.

РЕДАКТИРОВАТЬ: О вашем комментарии

Так много, когда вы проходите s в SayHello, вы создаете новую ссылку и обе ссылки в Main метод и в SayHello ссылаются на тот же объект. Но они существуют как две разные ссылки.

Если ты пишешь в

private static void SayHello(StringBuilder s)
{
    s.AppendLine("Hello");
    s = new StringBuilder();

}

Ссылка на в SayHello будет указывать на другой объект, но это ничего не изменит в методе Main, поскольку ссылка указывает на предыдущий объект, в котором вы написали Hello. Итак, снова вывод будет Hello.

Если вы хотите передать точную ссылку, вы должны использовать ref

Так что если вы напишите

private static void SayHello(ref StringBuilder s)
{
     s.AppendLine("Hello");
     s = null;
     //you will receive an exception in Main method, because now the value in the reference is null.
}

Рассмотрим следующую аналогию:

  1. Вы покупаете новую квартиру (new StringBuilder()) и вы получите ключ (s), который предоставляет вам доступ.
  2. Вы нанимаете уборщика (s.AppendLine) и вы даете ему копию своего ключа, чтобы он мог получить доступ к вашей квартире и очистить ее.
  3. Уборщик использует копию вашего ключа, убирает вашу квартиру и, поскольку ему так хочется, перепрограммирует ключ, чтобы открыть какую-то другую квартиру.
  4. Вы заканчиваете свою ежедневную работу и возвращаетесь домой. Ваш ключ просто открывает дверь в вашу квартиру, и вы находите ее чистой.

Это в основном то, что происходит, когда вы передаете ссылочный тип по значению.

Теперь рассмотрим следующее:

  1. Вы покупаете новую квартиру (new StringBuilder()) и вы получите ключ (s), который предоставляет вам доступ.
  2. Вы нанимаете уборщика (s.AppendLine) и вы даете ему свой ключ, чтобы он мог убрать вашу квартиру.
  3. Уборщик убирает вашу комнату и, потому что ему так хочется, перепрограммирует ключ, который вы дали ему, чтобы открыть какую-то другую квартиру. Он оставляет твой ключ под ковриком.
  4. Вы заканчиваете свою ежедневную работу и возвращаетесь домой. Вы пытаетесь использовать свой ключ, но дверь не открывается. Через окно видно, что ваша квартира чистая.

Вот что происходит, когда вы передаете ссылочный тип по ссылке.

Другие вопросы по тегам