考虑以下代码:
unsafe
{
string foo = string.Copy("This can't change");
fixed (char* ptr = foo)
{
char* pFoo = ptr;
pFoo[8] = pFoo[9] = ' ';
}
Console.WriteLine(foo); // "This can change"
}
这将创建一个指向foo
的第一个字符的指针,将其重新分配为可变的,并将8和9位置上的字符更改为' '
。
请注意,我实际上从未重新分配foo
;相反,我通过修改其状态或突变字符串的值来改变它的值。因此,.NET字符串是可变的。
事实上,以下代码可以正常工作:
unsafe
{
string bar = "Watch this";
fixed (char* p = bar)
{
char* pBar = p;
pBar[0] = 'C';
}
string baz = "Watch this";
Console.WriteLine(baz); // Unrelated, right?
}
这将会输出"Catch this"
,因为字符串字面量会被共享。
这有很多适用场景,例如:
string GetForInputData(byte[] inputData)
{
// allocate a mutable buffer...
char[] buffer = new char[inputData.Length];
// fill the buffer with input data
// ...and a string to return
return new string(buffer);
}
被替换为:
string GetForInputData(byte[] inputData)
{
// allocate a string to return
string result = new string('\0', inputData.Length);
fixed (char* ptr = result)
{
// fill the result with input data
}
return result; // return it
}
这可以在速度关键的领域(例如编码)中节省潜在的大量内存分配/性能成本。
我想你可以说这不算,因为它“使用了一个技巧”来使指针可变,但反过来说,正是 C# 语言设计者支持首先将字符串赋值给指针。(实际上,这在 String 和 StringBuilder 内部 一直 都 在 做,因此从技术上讲,您可以使用此方法制作自己的 StringBuilder。)
那么,.NET 字符串真的应该被认为是不可变的吗?
string
类的公共 API - 它所暴露的方法和属性。 - MarcinJuraszekfixed
返回例如ReadOnlyPtr<char>
的字符串,这样就无法强制转换为可变的呢? - James Ko