Подтвердить что ты не робот

Назначение строк в С#

Несколько недель назад я обнаружил, что строки в С# определяются как ссылочные типы, а не типы значений. Первоначально я был смущен этим, но потом после некоторого чтения я вдруг понял, почему важно хранить строки в куче, а не в стеке, потому что было бы очень неэффективно иметь очень большую строку, которая копируется по непредсказуемому номеру кадров стека. Я полностью согласен с этим.

Я чувствую, что мое понимание почти завершено, но есть один элемент, который мне не хватает - какая языковая функция использует строки, чтобы сохранить их неизменяемыми? Чтобы проиллюстрировать пример кода:

string valueA = "FirstValue";
string valueB = valueA;
valueA = "AnotherValue";

Assert.AreEqual("FirstValue", valueB); // Passes

Я не понимаю, какая языковая функция делает копию valueA, когда я назначаю ее значению B. Или, возможно, ссылка на valueA не изменяется, когда я назначаю ее значению B, только valueA получает новую ссылку на себя, когда я устанавливаю строку. Поскольку это тип экземпляра, я не понимаю, почему это работает.

Я понимаю, что вы можете перегрузить, например, операторы == и! =, но я не могу найти никакой документации по перегрузке операторов =. Какое объяснение?

4b9b3361

Ответ 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 получает новый ссылаться на себя, когда я устанавливаю строка.

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

Я не могу найти ни одного документация по перегрузке = операторы.

Это не из-за каких-либо недостатков на вашей стороне, потому что нет способа перегрузить оператор присваивания в С#.

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