C#中的字符串不变性

时间:2010-08-28 14:27:29

标签: c# .net pointers immutability

我很好奇StringBuilder类是如何在内部实现的,因此我决定查看Mono的源代码并将其与Reflector的反汇编代码进行比较。从本质上讲,Microsoft的实现使用char[]在内部存储字符串表示,并使用一堆不安全的方法来操作它。这很简单,没有提出任何问题。但当我发现Mono在StringBuilder中使用一个字符串时,我很困惑:

private int _length;
private string _str;

第一个想法是:“多么无谓的StringBuilder”。但后来我发现可以使用指针变异字符串:

public StringBuilder Append (string value) 
{
     // ...
     String.CharCopy (_str, _length, value, 0, value.Length);
}

internal static unsafe void CharCopy (char *dest, char *src, int count) 
{
    // ...
    ((short*)dest) [0] = ((short*)src) [0]; dest++; src++;
}    

我曾经用C / C ++编程一点,所以我不能说这段代码让我很困惑,但我认为字符串是完全不可变的(即绝对没有办法改变它)。所以实际问题是:

  • 我可以创建一个完全不可变的类型吗?
  • 除性能问题外,是否有任何理由使用此类代码? (更改不可变类型的不安全代码)
  • 字符串本身是否具有线程安全性?

6 个答案:

答案 0 :(得分:43)

答案 1 :(得分:3)

如果你不安全,也可以在C#中改变字符串(IIRC)。

答案 2 :(得分:3)

没有完全不可变的类型,一个不可变的类是因为它不允许任何外部代码改变它。使用反射或不安全代码,您仍然可以更改它的值。

您可以使用readonly关键字创建不可变变量,但这仅适用于值类型。如果在引用类型上使用它,它只是受保护的引用,而不是它指向的对象。

不可变类型有几个原因,如性能和稳健性。

已知字符串是不可变的(在StringBuilder之外)这一事实意味着编译器可以根据它进行优化。编译器永远不必生成复制字符串的代码,以防止它在作为参数传递时被更改。

从不可变类型创建的对象也可以在线程之间安全地传递。由于它们无法更改,因此不同的线程不会同时更改它们,因此无需同步访问它们。

不可变类型可用于避免编码错误。如果您知道不应该更改某个值,那么通常最好确保它不会被错误地更改。

答案 3 :(得分:2)

这里没有黑魔法。字符串类是不可变的,因为它没有任何允许您修改内部字符串的公共字段,属性或方法。任何改变字符串的方法都会返回一个新的字符串实例。你当然也可以用你自己的课程来做到这一点。

答案 4 :(得分:1)

  

我可以创建一个完全不可变的类型吗?

是。有一个构造函数来设置私有字段,只获取属性而没有方法。

  

除性能问题外,是否有任何理由使用此类代码?

一个例子:这样的类型不需要从多个并发线程安全地使用锁,这使得正确的代码更容易编写(没有锁定出错)。

附加:足够特权的代码总是可以绕过.NET保护:反射读取和写入私有字段,或者不安全代码直接操作对象的内存。

这在.NET之外是正确的,一个特权进程(即具有一个“上帝”特权的进程或线程令牌,例如启用Take Ownership)可以进入任何其他进程加载dll,注入运行任意代码的线程,读或写内存(包括覆盖执行预防等)。系统的完整性与系统所有者的合作一样强大。

答案 5 :(得分:1)