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

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

Ответ 1

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

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

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

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

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

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

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

Если вы передаете параметр ЛЮБОГО типа и не используете ключевое слово 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 5 on objMain

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

А, но...

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

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

Дополнительные ресурсы:

Ответ 2

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

Ответ 3

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

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

Ответ 4

В С# все простые типы данных передаются по значению, если не использовать ключевое слово 'ref' для передачи по ссылке. Экземпляры системы и пользовательского класса обычно передаются по ссылке.