为什么修改这个字符串也会修改另一个单独的字符串的内容?

Why is modifying this String also modifying the content of another, seperate String?

在我的静态 main 函数中,我有以下代码:

string str1 = "aaaaaaaaa";
pointerTest();
Console.WriteLine( "str1 is: " + str1 );

声明为 unsafe 的静态 pointerTest 方法包含以下内容:

string str2 = "aaaaaaaaa";
fixed( char* ptr = str2 )
{
    for( int i = 0; i < str2.Length / 3; ++i )
        ptr[i] = 'z';
}

Console.WriteLine( "str2 is: " + str2 );

请注意 str1str2 是如何独立声明的,但确实具有相同的内容。

该程序的预期输出将是:

str2 is: zzzaaaaaa
str1 is: aaaaaaaaa

当我运行程序实际输出显示如下:

str2 is: zzzaaaaaa
str1 is: zzzaaaaaa

当我将 str2str1 更改为不具有完全相同的内容时(例如,在 [=18 的末尾添加一个 'a' =]) 程序按预期运行。
如果发现此行为同时存在于 .Net Core 3.1Mono 中(不确定确切版本,我使用 Repl.It

我的问题是为什么会发生这种行为以及可以采取什么措施来解决它。

我的理论是,这是因为 编译器优化 ,特别是一个名为 string interning:

的过程

编译器认识到没有必要自行分配 str2,因为内存中已经存在完全相同的 chars 序列,并随初始化分配str1 个。因此,它没有重新分配它,而是使 str2 成为 str1 已经指向的位置的引用。有关更多信息,请阅读 here.

字符串 在 C# 中被认为是不可变的,因此 - 在正常情况下 - 不应该以任何方式、形状或形式修改它们的内容。

由于此代码使用了 unsafe 关键字和 pointer-logic,因此不能保证不会导致 未定义的行为,从而导致令人惊讶的结果。

解决这个“问题”的唯一方法是遵守 C# 规范并将 Strings 视为不可变的。