Назначение строк в С#
Несколько недель назад я обнаружил, что строки в С# определяются как ссылочные типы, а не типы значений. Первоначально я был смущен этим, но потом после некоторого чтения я вдруг понял, почему важно хранить строки в куче, а не в стеке, потому что было бы очень неэффективно иметь очень большую строку, которая копируется по непредсказуемому номеру кадров стека. Я полностью согласен с этим.
Я чувствую, что мое понимание почти завершено, но есть один элемент, который мне не хватает - какая языковая функция использует строки, чтобы сохранить их неизменяемыми? Чтобы проиллюстрировать пример кода:
string valueA = "FirstValue";
string valueB = valueA;
valueA = "AnotherValue";
Assert.AreEqual("FirstValue", valueB); // Passes
Я не понимаю, какая языковая функция делает копию valueA, когда я назначаю ее значению B. Или, возможно, ссылка на valueA не изменяется, когда я назначаю ее значению B, только valueA получает новую ссылку на себя, когда я устанавливаю строку. Поскольку это тип экземпляра, я не понимаю, почему это работает.
Я понимаю, что вы можете перегрузить, например, операторы == и! =, но я не могу найти никакой документации по перегрузке операторов =. Какое объяснение?
Ответы
Ответ 1
какая функция языка использует строки, чтобы сохранить их неизменяемыми?
Это не язык. Это способ определения класса.
Например,
class Integer {
private readonly int value;
public int Value { get { return this.value; } }
public Integer(int value) { this.value = value; } }
public Integer Add(Integer other) {
return new Integer(this.value + other.value);
}
}
похож на int
за исключением ссылочного типа, но он неизменен. Мы определили это так. Мы также можем определить, что это тоже можно изменить:
class MutableInteger {
private int value;
public int Value { get { return this.value; } }
public MutableInteger(int value) { this.value = value; } }
public MutableInteger Add(MutableInteger other) {
this.value = this.value + other.value;
return this;
}
}
См?
Я не понимаю, какая языковая функция делает копию valueA
, когда я назначаю ее valueB
.
Он не копирует string
, он копирует ссылку. string
являются ссылочным типом. Это означает, что переменными типа string
являются места хранения, значения которых являются ссылками. В этом случае их значения являются ссылками на экземпляры string
. Когда вы назначаете переменную типа string
другому типу string
, значение копируется. В этом случае это значение является ссылкой и копируется по заданию. Это справедливо для любого ссылочного типа, а не только string
или только неизменяемых ссылочных типов.
Или, возможно, ссылка на valueA
не изменяется, когда я назначаю ее valueB
, только valueA
получает новую ссылку на себя, когда я устанавливаю строку.
Нет, значения valueA
и valueB
относятся к тому же экземпляру string
. Их значения - это ссылки, и эти значения равны. Если бы вы могли каким-то образом мутировать * экземпляр string
, на который ссылается valueA
, ссылка как valueA
, так и valueB
увидит эту мутацию.
Поскольку это тип экземпляра, я не понимаю, почему это работает.
Нет такой вещи, как тип экземпляра.
В основном, string
являются ссылочными типами. Но string
неизменяемы. Когда вы мутируете string
, происходит то, что вы получаете ссылку на новую строку, являющуюся результатом мутации, на уже существующую string
.
string s = "hello, world!";
string t = s;
string u = s.ToUpper();
Здесь s
и t
- это переменные, значения которых относятся к тому же экземпляру string
. Ссылка s
не мутируется вызовом String.ToUpper
. Вместо этого s.ToUpper
выполняет мутацию реферата s
и возвращает ссылку на новый экземпляр string
, который он создает в процессе применения мутации. Мы присваиваем эту ссылку u
.
Я понимаю, что вы можете перегрузить, например, операторы == и! =, но я не могу найти никакой документации по перегрузке операторов =.
Вы не можете перегрузить =
.
* Вы можете с некоторыми трюками. Игнорируйте их.
Ответ 2
Прежде всего, ваш пример будет работать одинаково с любыми ссылочными переменными, а не только с строками.
Что происходит:
string valueA = "FirstValue"; //ValueA is referenced to "FirstValue"
string valueB = valueA; //valueB references to what valueA is referenced to which is "FirstValue"
valueA = "AnotherValue"; //valueA now references a new value: "AnotherValue"
Assert.AreEqual("FirstValue", valueB); // remember that valueB references "FirstValue"
Теперь неизменность - это другая концепция. Это означает, что само значение не может быть изменено.
Это проявится в такой ситуации:
string valueA = "FirstValue"; //ValueA is referenced to "FirstValue"
string valueB = valueA; //valueB references to what valueA is referenced to which is "FirstValue"
valueA.Replace('F','B'); //valueA will now be: "BirstValue"
Assert.AreEqual("FirstValue", valueB); // remember that valueB references "FirstValue"
Это из-за неизменяемости String, valueA не меняет сама строку... Она создает новую COPY с изменениями и ссылками, которые.
Ответ 3
Или, возможно, ссылка на valueA не изменяется, когда я назначаю его valueB, только valueA получает новый ссылаться на себя, когда я устанавливаю строка.
Это правильно. Поскольку строки неизменяемы, нет проблем с наличием двух переменных, ссылающихся на один и тот же строковый объект. Когда вы назначаете новую строку одному из них, это заменяемая ссылка, а не строковый объект.
Я не могу найти ни одного документация по перегрузке = операторы.
Это не из-за каких-либо недостатков на вашей стороне, потому что нет способа перегрузить оператор присваивания в С#.
Оператор =
довольно прост, он принимает значение с правой стороны и присваивает переменной с левой стороны. Если это ссылочный тип, это значение является ссылкой, поэтому это назначено.