请考虑以下代码:
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
}
如果您在速度关键字段(例如编码)中工作,这可以节省潜在的巨大内存分配/性能成本。
我想你可以说这不算数,因为它"使用hack"使指针变为可变,但是再一次是C#语言设计者支持首先将字符串赋值给指针。 (事实上,这是在String
和StringBuilder
内部all the time完成的,因此从技术上讲,您可以使用此方法创建自己的StringBuilder。)
那么,.NET字符串真的应该被认为是不可变的吗?
答案 0 :(得分:6)
§18.6的C#语言规范( fixed
语句)专门解决了通过固定指针修改字符串的情况,并指出这样做会导致未定义的行为:
通过固定指针修改托管类型的对象可能导致未定义的行为。例如,因为字符串是不可变的,所以程序员有责任确保不修改指向固定字符串的指针所引用的字符。
答案 1 :(得分:1)
我只需要玩这个并尝试确认 string literal 的地址是否指向同一个内存位置。
结果是:
string foo = "Fix value?"; //New address: 0x02b215f8
string foo2 = "Fix value?"; //Points to same address: 0x02b215f8
string fooCopy = string.Copy(foo); //New address: 0x021b2888
fixed (char* p = foo)
{
p[9] = '!';
}
Console.WriteLine(foo);
Console.WriteLine(foo2);
Console.WriteLine(fooCopy);
//Reference is equal, which means refering to same memory address
Console.WriteLine(string.ReferenceEquals(foo, foo2)); //true
//Reference is not equal, which creates another string in new memory address
Console.WriteLine(string.ReferenceEquals(foo, fooCopy)); //false
我们看到foo
初始化一个字符串文字,指向我PC中的0x02b215f8
内存地址。为foo2
分配相同的字符串文字引用相同的内存地址。并且创建相同字符串文字的副本会产生一个新副本。通过string.ReferenceEquals()
进行的进一步测试表明,foo
和foo2
确实相同,foo
和fooCopy
的参考不同。
有趣的是,字符串文字如何在内存中进行操作,并影响其他仅引用它的变量。由于存在这种行为,我们应该注意的一件事。