Я знаю, что «строка» в C # является ссылочным типом. Это на MSDN. Однако этот код не работает так, как должен:
class Test
{
public static void Main()
{
string test = "before passing";
Console.WriteLine(test);
TestI(test);
Console.WriteLine(test);
}
public static void TestI(string test)
{
test = "after passing";
}
}
Выходные данные должны быть «перед передачей» «после передачи», так как я передаю строку в качестве параметра и она является ссылочным типом, второй оператор вывода должен распознавать, что текст изменился в методе TestI. Тем не менее, я получаю «перед передачей», «перед передачей», создавая впечатление, что оно передается по значению, а не по ссылке. Я понимаю, что строки неизменны, но я не понимаю, как это объясняет, что здесь происходит. Чего мне не хватает? Спасибо.
Ответы:
Ссылка на строку передается по значению. Существует большая разница между передачей ссылки по значению и передачей объекта по ссылке. К сожалению, слово «ссылка» используется в обоих случаях.
Если вы делаете передать ссылку строки по ссылке, он будет работать , как вы ожидаете:
Теперь вам нужно провести различие между внесением изменений в объект, на который ссылается ссылка, и внесением изменения в переменную (например, параметр), чтобы позволить ей ссылаться на другой объект. Мы не можем вносить изменения в строку, потому что строки неизменны, но мы можем продемонстрировать это с помощью
StringBuilder
:Смотрите мою статью о передаче параметров для более подробной информации.
источник
referenced
это в качестве своего ответаЕсли мы должны ответить на вопрос: String является ссылочным типом и ведет себя как ссылка. Мы передаем параметр, который содержит ссылку, а не фактическую строку. Проблема в функции:
Параметр
test
содержит ссылку на строку, но это копия. У нас есть две переменные, указывающие на строку. И поскольку любые операции со строками фактически создают новый объект, мы делаем нашу локальную копию, чтобы указывать на новую строку. Но исходнаяtest
переменная не изменилась.Предлагаемые решения для
ref
включения в объявление функции и в вызов работают, потому что мы не передадим значениеtest
переменной, а передадим просто ссылку на нее. Таким образом, любые изменения внутри функции будут отражать исходную переменную.Я хочу повторить в конце: String является ссылочным типом, но поскольку он неизменный, строка
test = "after passing";
фактически создает новый объект, и наша копия переменнойtest
изменяется, чтобы указывать на новую строку.источник
Как уже говорили другие,
String
тип в .NET является неизменным, и его ссылка передается по значению.В исходном коде, как только эта строка выполнится:
затем
test
больше не ссылается на исходный объект. Мы создали новыйString
объект и назначеныtest
для ссылки на этот объект в управляемой куче.Я чувствую, что многие люди запутались здесь, потому что нет видимого формального конструктора, который бы напоминал им. В этом случае это происходит негласно, так как
String
тип имеет языковую поддержку в том, как он построен.Следовательно, именно поэтому изменение
test
не видно вне области действияTestI(string)
метода - мы передали ссылку по значению, и теперь это значение изменилось! Но еслиString
ссылка была передана по ссылке, то, когда ссылка изменилась, мы увидим ее вне области действияTestI(string)
метода.В этом случае необходимо использовать ключевое слово ref или out . Я чувствую, что
out
ключевое слово может быть немного лучше подходит для этой конкретной ситуации.источник
out
должен быть назначен в методе для кода для компиляции.ref
не имеет такого требования. Такжеout
параметры инициализируются вне метода - код в этом ответе является контрпримером.out
параметры могут быть инициализированы вне метода, но не обязательно. В этом случае мы хотим инициализироватьout
параметр, чтобы продемонстрировать точку зрения о природеstring
типа в .NET.На самом деле это было бы то же самое для любого объекта в этом отношении, то есть быть ссылочным типом и передавать по ссылке - это две разные вещи в c #.
Это будет работать, но это применимо независимо от типа:
Также о строке, являющейся ссылочным типом, это также специальный. Он разработан так, чтобы быть неизменным, поэтому все его методы не будут изменять экземпляр (они возвращают новый). В нем также есть некоторые дополнительные вещи для производительности.
источник
Вот хороший способ подумать о разнице между типами значений, передачей по значению, ссылочными типами и передачей по ссылке:
Переменная является контейнером.
Переменная типа значения содержит экземпляр. Переменная ссылочного типа содержит указатель на экземпляр, хранящийся в другом месте.
Изменение переменной типа значения изменяет экземпляр, который она содержит. Изменение переменной ссылочного типа изменяет экземпляр, на который она указывает.
Отдельные переменные ссылочного типа могут указывать на один и тот же экземпляр. Следовательно, один и тот же экземпляр может быть видоизменен через любую переменную, которая указывает на него.
Переданный по значению аргумент - это новый контейнер с новой копией содержимого. Переданный по ссылке аргумент - это оригинальный контейнер с его исходным содержимым.
Когда аргумент типа значения передается по значению: переназначение содержимого аргумента не оказывает влияния вне области действия, поскольку контейнер является уникальным. Изменение аргумента не оказывает влияния вне области действия, поскольку экземпляр является независимой копией.
Когда аргумент ссылочного типа передается по значению: переназначение содержимого аргумента не оказывает влияния вне области действия, поскольку контейнер является уникальным. Изменение содержимого аргумента влияет на внешнюю область, поскольку скопированный указатель указывает на общий экземпляр.
Когда какой-либо аргумент передается по ссылке: переназначение содержимого аргумента влияет на внешнюю область, поскольку контейнер является общим. Изменение содержимого аргумента влияет на внешнюю область, поскольку содержимое является общим.
В заключение:
Строковая переменная является переменной ссылочного типа. Следовательно, он содержит указатель на экземпляр, хранящийся в другом месте. При передаче по значению его указатель копируется, поэтому изменение строкового аргумента должно повлиять на общий экземпляр. Однако строковый экземпляр не имеет изменяемых свойств, поэтому строковый аргумент не может быть изменен в любом случае. При передаче по ссылке контейнер указателя является общим, поэтому переназначение все равно будет влиять на внешнюю область.
источник
« Картинка стоит тысячи слов ».
У меня есть простой пример, он похож на ваш случай.
Это то, что случилось:
s1
иs2
переменные ссылаются на один и тот же"abc"
строковый объект."abc"
строковый объект не изменяет себя (to"def"
), но"def"
вместо этого создается новый строковый объект, а затемs1
ссылки на него.s2
все еще ссылки на"abc"
строковый объект, так что это вывод.источник
Приведенные выше ответы полезны, я просто хотел бы добавить пример, который, я думаю, четко демонстрирует, что происходит, когда мы передаем параметр без ключевого слова ref, даже если этот параметр является ссылочным типом:
источник
Для любопытных умов и для завершения разговора: Да, String является ссылочным типом :
Но учтите, что это изменение работает только в небезопасном блоке! потому что строки неизменяемы (из MSDN):
И имейте в виду, что:
источник
Я считаю, что ваш код аналогичен следующему, и вы не должны были ожидать, что значение изменилось по той же причине, что и здесь:
источник
Пытаться:
источник