C#中的字符串赋值

时间:2011-06-04 11:50:11

标签: c# string reference heap

几周前,我发现C#中的字符串被定义为引用类型而不是值类型。最初我对此感到困惑,但经过一些阅读后,我突然明白了为什么将字符串存储在堆而不是堆栈上是很重要的 - 因为将一个非常大的字符串复制到一个不可预测的数字上是非常低效的堆栈帧。我完全接受了这一点。

我觉得我的理解几乎已经完成,但是我缺少一个元素 - 字符串用什么语言来保持它们不变?用代码示例说明:

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

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

当我将valueA分配给valueB时,我不明白哪个语言功能会复制valueA。或者,当我将valueA赋值给valueB时,对valueA的引用不会改变,只有valueA在设置字符串时才会获得对自身的新引用。由于这是一个实例类型,我不明白为什么会这样。

我知道你可以重载,例如,==和!=运算符,但我似乎找不到有关重载=运算符的任何文档。解释是什么?

3 个答案:

答案 0 :(得分:14)

  

字符串使用什么语言功能来保持它们不可变?

这不是语言功能。这是课程的定义方式。

例如,

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在设置字符串时才会获得对自身的新引用。

不,stringstring的值引用valueA的相同实例。它们的值是引用,这些值是相等的。如果你能以某种方式改变 * valueA引用的valueB实例,那么stringstring的引用都会看到这种突变。

  

由于这是一个实例类型,我不明白为什么会这样。

没有实例类型。

基本上,string是引用类型。但string是不可改变的。当您改变string s = "hello, world!"; string t = s; string u = s.ToUpper(); 时,会发生的情况是您获得对新字符串的引用,该字符串是已存在的s的突变结果。

t

此处,strings是其值引用String.ToUpper的同一个实例的变量。调用s.ToUpper时,s的引用不会发生变异。相反,stringu的引用进行了变异,并返回对它在应用变异过程中创建的新=实例的引用。我们将该引用分配给{{1}}。

  

我知道你可以重载,例如,==和!=运算符,但我似乎找不到有关重载=运算符的任何文档。

您无法重载{{1}}。

* 你可以用一些技巧。忽略它们。

答案 1 :(得分:6)

首先,您的示例将对任何引用变量起作用,而不仅仅是字符串。

会发生什么:

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"

现在immutability是一个不同的概念。这意味着价值本身无法改变 这将出现在这样的情况:

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 ,其中包含更改和引用。

答案 2 :(得分:3)

  

或许,对valueA的引用   分配给它时不会改变   valueB,只有valueA获得一个新的   当我设置时,引用自身   字符串。

这是正确的。由于字符串是不可变的,因此引用相同字符串对象的两个变量没有问题。当您为其中一个分配新字符串时,它是被替换的引用,而不是字符串对象。

  

我似乎找不到任何东西   有关重载=的文档   运算符。

这不是由于你方面的任何缺点,这是因为没有办法在C#中重载赋值运算符。

=运算符非常简单,它取右侧的值并分配给左侧的变量。如果它是引用类型,则该值是引用,因此这就是已分配的内容。