c#-4.0 - 为什么当引用更改时,可变 StringBuilder 的行为类似于不可变字符串?

标签 c#-4.0 immutability stringbuilder reference-type

Albarharis 所著的《C# 4.0 IN A NUTSHELL》第 4 版书第 249 页指出: “...调用 object.ReferenceEquals 保证正常的引用相等。”

所以,我决定对此进行测试。

首先我尝试了这样的值类型。

 int aa1 = 5;
 int aa2 = aa1;
MessageBox.Show("object.ReferenceEquals(aa1,aa2) is: " + object.ReferenceEquals(aa1, aa2)); 

正如我所料,结果是错误的: object.ReferenceEquals(aa1, aa2) 是: False

生活是美好的。 然后我尝试了像这样的可变引用类型。

System.Text.StringBuilder sblr1 = new System.Text.StringBuilder();
sblr1.Append("aaa");
System.Text.StringBuilder sblr2 = sblr1;          
MessageBox.Show("object.ReferenceEquals(sblr1,sblr2) is: " + object.ReferenceEquals(sblr1, sblr2)); 

正如我所料,结果是真的 object.ReferenceEquals(sblr1, sblr2) 是:True

生活还是很美好的。 然后我想,既然它是一个可变引用类型,那么如果我将一个变量更改为 null,那么两个变量都应该为 null。 所以我尝试了以下方法。

System.Text.StringBuilder sblr1 = new System.Text.StringBuilder();
sblr1.Append("aaa");
System.Text.StringBuilder sblr2 = sblr1;
sblr1 = null;
MessageBox.Show("object.ReferenceEquals(sblr1,sblr2) is: " + object.ReferenceEquals(sblr1, sblr2)); 

我希望它们都为空。 但我得到的结果是False: object.ReferenceEquals(sblr1, sblr2) 是: False

现在的生活不太好。 我认为如果它覆盖了 sblr1 的内存位置,那么它也会覆盖 sblr2 的内存位置。

然后我想也许它们指向两个不同的空值,所以我尝试了这个:

System.Text.StringBuilder sblr1 = new System.Text.StringBuilder();
sblr1.Append("aaa");
System.Text.StringBuilder sblr2 = sblr1;
sblr2 = null;
MessageBox.Show("sblr1 == " + sblr1 + " and sblr2 == " + sblr2);

但在这里,只有一个像这样指向 null。 sblr1 == aaa 和 sblr2 ==

只有一个为空。

它显示了我期望的不可变引用类型(如字符串对象)的行为。 使用字符串对象,我可以执行以下操作:

string aa1 = "aaX";
string aa2 = "aaX";
MessageBox.Show("object.ReferenceEquals(aa1,aa2) is: " + object.ReferenceEquals(aa1, aa2)); 

他们都会像这样引用相同的东西。 object.ReferenceEquals(aa1, aa2) 是:True 因为“aaX”只写入程序集一次。

但是如果我这样做:

string aa1 = "aaX";
string aa2 = "aaX"; 
aa1 = null; 
MessageBox.Show("After aa1 is null(" + aa1 + "), then aa2 is: " + aa2);

然后他们指出了不同的事情,例如: aa1为null()后,则aa2为:aaX

这是因为字符串对象是不可变的。内存位置不会被覆盖。相反,该变量指向堆内存中新值所在的不同位置。 在上面的示例中将 aa1 更改为 null 意味着 aa1 将指向堆内存上的不同位置。

为什么可变引用类型的行为与不可变引用类型相同?

<小时/>

编辑下午 4:03 和 4:08

我最近尝试过这个:

System.Text.StringBuilder sblr1 = new System.Text.StringBuilder();
sblr1.Append("aaa");

// sblr1 and sblr2 should now both point to the same location on the Heap that has "aaa".
System.Text.StringBuilder sblr2 = sblr1;

System.Text.StringBuilder sblr3 = new System.Text.StringBuilder();
sblr3.Append("bbb");

sblr1 = sblr3;
MessageBox.Show("sblr1 == " + sblr1 + " and sblr2 == " + sblr2 + " and sblr3 == " + sblr3);

这给了我: sblr1 == bbb 和 sblr2 == aaa 和 sblr3 == bbb

这更像是我期待的结果。 我现在明白了,多亏了这些评论,我无意中期望 null 充当一个内存位置。

最佳答案

I thought that if it overrode the memory location of sblr1, then it would be overriding the memory location of sblr2 also.

这是你的误解。

当你写下这个:

System.Text.StringBuilder sblr2 = sblr1;

您将 sblr2 变量指定为对 StringBuilder 实例的引用,与 sblr1 所指向的实例相同。这两个变量现在指向同一个引用。

然后你写:

sblr1 = null;

这会将 sblr1 变量更改为空引用。您根本没有更改内存中的实例。

这与引用是否是可变类型无关。您正在更改变量,而不是它们引用的实例。

至于你的字符串示例:

That's because string objects are immutable. The memory location doesn't get overridden

这实际上不是真的。将一个字符串变量设置为 null 这一事实与字符串不可变性没有任何关系。这是一个单独的问题。

Why then is the mutable reference type behaving just the same as the immutable reference type?

您看到的行为与可变性无关。这是所有引用类型(无论是不可变的还是可变的)的标准行为。可变性是一个不同的问题。

可变性的主要问题是:

假设您有一个类,如下所示:

class Foo
{
    public int Bar { get; set; }
}

如果你这样写:

Foo a = new Foo();
a.Bar = 42;
Foo b = a;
b.Bar = 54;
Console.WriteLine(a.Bar); // Will print 54, since you've changed the same mutable object

对于不可变类型,这种情况不会发生,因为您无法更改 Bar - 相反,如果您创建一个不可变类:

class Baz
{
    public Baz(int bar) { this.Bar = bar; }

    public int Bar { get; private set; }
}

你需要写:

Baz a = new Baz(42);
Baz b = a;

// This isn't legal now:
// b.Bar = 54;
// So you'd write:
b = new Baz(54); // Creates a new reference

或者,您可以使类在“更改”操作上返回新引用,即:

class Baz
{
    public Baz(int bar) { this.Bar = bar; }

    public int Bar { get; private set; }

    public Baz Alter(int newValue) { return new Baz(newValue); } // May copy other data from "this"
}

然后当你写:

Baz a = new Baz(42);
Baz b = a.Alter(54); // b is now a new instance

这就是 string 发生的情况 - 所有方法都会返回一个新实例,因为字符串是不可变的,因此您永远无法“更改”现有副本。

关于c#-4.0 - 为什么当引用更改时,可变 StringBuilder 的行为类似于不可变字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15841748/

相关文章:

java - 我可以更改传递给我的方法的字符串对象的值吗?

algorithm - 具有不可变状态的二维数组处理

c++ - C# 的 readonly 字段修饰符的 C++ 等价物是什么?

java - 将 Stringbuffer 转换为 String() 后调用 String subSequence()

stringbuilder - Apache Common 的 StrBuilder 和 Java 的 StringBuilder 有什么区别?

c#-4.0 - 如何获得程序集的强名称?

c# - C#如何改变数据表中所有列的数据类型

c# - 无法将类型为 '<XYPairsInRange>d__8' 的对象转换为类型 'IXYPair'

javascript - 根据 Iframe 的 src URL 的 Iframe 内容动态调整 Iframe 的大小

java - 如何在使用 java 显示数据库中的数据时将文本水平居中