String是一种引用类型,即使它具有值类型的大多数特性,例如是不可变的并且具有==重载以比较文本而不是确保它们引用相同的对象.
为什么字符串不是一个值类型呢?
字符串不是值类型,因为它们可能很大,并且需要存储在堆上.存储在堆栈上的值类型(在CLR的所有实现中).堆栈分配字符串会破坏各种各样的东西:32位只有1MB,64位只有4MB,你必须打包每个字符串,导致复制惩罚,你不能实习字符串和内存使用会气球等...
(编辑:添加了关于值类型存储作为实现细节的说明,这导致了这种情况,我们有一个类型的值没有继承自System.ValueType的值.感谢Ben.)
它不是一个值类型,因为如果它是一个值类型,性能(空间和时间!)将是可怕的,并且每次传递给方法等返回它的值时都必须复制它的值.
它具有保持世界理智的价值语义.你能想象编码是多么困难吗?
string s = "hello"; string t = "hello"; bool b = (s == t);
设置b
为false
?想象一下,编写任何应用程序的难度都很大.
引用类型和值类型之间的区别基本上是语言设计中的性能折衷.引用类型在构造和销毁以及垃圾收集方面有一些开销,因为它们是在堆上创建的.另一方面,值类型在方法调用上有开销(如果数据大小大于指针),因为整个对象被复制而不仅仅是指针.因为字符串可以(并且通常是)远大于指针的大小,所以它们被设计为引用类型.此外,正如Servy所指出的那样,值类型的大小必须在编译时知道,而字符串并不总是如此.
可变性问题是一个单独的问题.引用类型和值类型都可以是可变的或不可变的.值类型通常是不可变的,因为可变值类型的语义可能会令人困惑.
引用类型通常是可变的,但如果有意义,可以设计为不可变的.字符串被定义为不可变的,因为它使某些优化成为可能.例如,如果在同一程序中多次出现相同的字符串文字(这很常见),编译器可以重用相同的对象.
那么为什么"=="重载以按文本比较字符串呢?因为它是最有用的语义.如果两个字符串相等,则由于优化,它们可能是也可能不是同一个对象引用.所以比较参考文献是没有用的,而比较文本几乎总是你想要的.
更一般地说,字符串具有所谓的价值语义.这是一个比值类型更通用的概念,它是C#特定的实现细节.值类型具有值语义,但引用类型也可能具有值语义.当类型具有值语义时,您无法确定底层实现是引用类型还是值类型,因此您可以考虑实现细节.
这是对旧问题的一个迟到的答案,但所有其他答案都忽略了这一点,即.NET在2005年的.NET 2.0之前没有泛型.
String
是一种引用类型而不是值类型,因为对于Microsoft来说确保字符串可以以非常有效的方式存储在非泛型集合中是至关重要的,例如System.Collection.ArrayList
.
在非泛型集合中存储值类型需要特殊转换object
为称为装箱的类型.当CLR选择一个值类型时,它将值包装在a中System.Object
并将其存储在托管堆上.
从集合中读取值需要反向操作,称为拆箱.
装箱和拆箱都有不可忽视的成本:装箱需要额外的分配,拆箱需要进行类型检查.
一些答案声称错误,string
因为它的大小是可变的,所以它永远不会被实现为值类型.实际上,使用小字符串优化策略将字符串实现为固定长度的数据结构很容易:字符串将作为Unicode字符序列直接存储在内存中,除了将作为指向外部缓冲区的指针存储的大字符串.两种表示都可以设计成具有相同的固定长度,即指针的大小.
如果泛型从第一天开始就存在,我想将字符串作为值类型可能是一个更好的解决方案,具有更简单的语义,更好的内存使用和更好的缓存局部性.List
仅包含小字符串的A 可能是单个连续的内存块.
不仅字符串是不可变的引用类型. 多播代表也是如此. 这就是写作安全的原因
protected void OnMyEventHandler() { delegate handler = this.MyEventHandler; if (null != handler) { handler(this, new EventArgs()); } }
我认为字符串是不可变的,因为这是使用它们并分配内存的最安全的方法.为什么他们不是价值类型?以前的作者对堆栈大小等是正确的.我还要补充说,在程序中使用相同的常量字符串时,使字符串成为引用类型允许节省程序集大小.如果你定义
string s1 = "my string"; //some code here string s2 = "my string";
有可能"my string"常量的两个实例只会在程序集中分配一次.
如果您想像通常的引用类型一样管理字符串,请将字符串放在新的StringBuilder(字符串s)中.或者使用MemoryStreams.
如果要创建一个库,您希望在函数中传递一个巨大的字符串,可以将参数定义为StringBuilder或Stream.
此外,实现字符串的方式(每个平台不同)以及何时开始将它们拼接在一起.喜欢使用StringBuilder
.它为你分配一个缓冲区,一旦你到达目的地,它就会为你分配更多的内存,希望如果你做大的连接性能不会受到阻碍.
也许Jon Skeet可以在这里帮忙吗?
这主要是性能问题.
让字符串表现为LIKE值类型有助于编写代码,但将其作为值类型会产生巨大的性能损失.
要深入了解一下,请查看.net框架中有关字符串的精彩文章.