Как строки передаются в.NET?

Когда я прохожу string на функцию, является указателем на переданное содержимое строки или вся строка передана функции в стеке, как struct было бы?

3 ответа

Решение

Чтобы ответить на ваш вопрос, рассмотрите следующий код:

void Main()
{
    string strMain = "main";
    DoSomething(strMain);
    Console.Write(strMain); // What gets printed?
}
void DoSomething(string strLocal)
{
    strLocal = "local";
}

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

  1. Строки являются ссылочными типами в C#. Но это только часть картины.
  2. Они также неизменны, поэтому каждый раз, когда вы делаете что-то, что похоже на изменение строки, это не так. Создается совершенно новая строка, на нее указывает ссылка, а старая отбрасывается.
  3. Даже если строки являются ссылочными типами, strMain не передается по ссылке. Это ссылочный тип, но ссылка передается по значению. Это сложное различие, но оно очень важно. Каждый раз, когда вы передаете параметр без ref ключевое слово (не считая out параметры), вы передали что-то по значению.

Но что это значит?

Передача ссылочных типов по значению: вы уже делаете это

В C# есть две группы типов данных: ссылочные типы и типы значений. Есть также два способа передачи параметров в C#: по ссылке и по значению. Они звучат одинаково и их легко спутать. Они НЕ одно и то же!

Если вы передаете параметр ЛЮБОГО типа, и вы не используете ref ключевое слово, то вы передали его по значению. Если вы передали его по значению, то вы действительно передали его копию. Но если параметр был ссылочным типом, то вы скопировали ссылку, а не то , на что он указывал.

Вот первая строчка нашего Main метод:

string strMain = "main";

На самом деле в этой строке мы создали две вещи: строку со значением main где-то хранится в памяти, а ссылочная переменная называется strMain указывая на это.

DoSomething(strMain);

Теперь мы передаем эту ссылку DoSomething, Мы передали его по значению, что означает, что мы сделали копию. Но это ссылочный тип, так что это означает, что мы скопировали ссылку, а не саму строку. Теперь у нас есть две ссылки, каждая из которых указывает на одно и то же значение в памяти.

Внутри звонящего

Вот вершина DoSomething метод:

void DoSomething(string strLocal)

нет ref Ключевое слово, как обычно. Так strLocal не strMain, но они оба указывают на одно и то же место. Если мы "изменим" strLocal, как это...

strLocal = "local";   

... мы не изменили сохраненное значение как таковое. Мы повторно указали ссылку. Мы взяли ссылку под названием strLocal и направил его на совершенно новую строку. Что происходит с strMain когда мы это сделаем? Ничего такого. Это все еще указывает на старую строку!

string strMain = "main"; //Store a string, create a reference to it
DoSomething(strMain);    //Reference gets copied, copy gets re-pointed
Console.Write(strMain);  //The original string is still "main" 

Неизменность важна

Давайте изменим сценарий на секунду. Представьте, что мы работаем не со строками, а с некоторым изменяемым ссылочным типом, например, с классом, который вы создали.

class MutableThing
{
    public int ChangeMe { get; set; }
}

Если вы следуете по ссылке objLocal к объекту, на который он указывает, вы можете изменить его свойства:

void DoSomething(MutableThing objLocal)
{
     objLocal.ChangeMe = 0;
} 

Там еще только один MutableThing в памяти, и скопированная ссылка и исходная ссылка все еще указывают на это. Свойства MutableThing Сам изменился

void Main()
{
    var objMain = new MutableThing();
    objMain.ChangeMe = 5; 
    Console.Write(objMain.ChangeMe);  //it's 5 on objMain

    DoSomething(objMain);             //now it's 0 on objLocal
    Console.Write(objMain.ChangeMe);  //it's also 0 on objMain   
}

Ах, но...

... строки неизменны! Нет никаких ChangeMe свойство для установки. Ты не можешь сделать strLocal[3] = 'H'; как вы могли бы с массивом символов в стиле C; вместо этого вы должны создать совершенно новую строку. Единственный способ изменить strLocal это указать ссылку на другую строку, и это ничего не значит, что вы делаете strLocal может повлиять на strMain, Значение является неизменным, а ссылка является копией.

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

Другие ресурсы:

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

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

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

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