String是一种引用类型,尽管它具有值类型的大多数特征,例如不可变和重载==以比较文本,而不是确保它们引用同一个对象。

为什么字符串不只是一个值类型呢?


当前回答

简单地说,任何具有一定大小的值都可以被视为值类型。

其他回答

字符串不是值类型,因为它们可能很大,需要存储在堆上。值类型(在CLR的所有实现中)存储在堆栈上。堆栈分配字符串会破坏各种各样的事情:32位的堆栈只有1MB, 64位的堆栈只有4MB,你必须装箱每个字符串,导致复制惩罚,你不能实习字符串,内存使用会膨胀,等等……

(编辑:增加了关于值类型存储是实现细节的说明,这导致了这种情况,即我们有一个具有值语义的类型没有从System.ValueType继承。由于本。)

实际上,字符串与值类型几乎没有相似之处。对于初学者来说,并不是所有的值类型都是不可变的,你可以随心所欲地改变Int32的值,而它在堆栈上的地址仍然是相同的。

字符串是不可变的有一个很好的理由,这与它是引用类型无关,但与内存管理有很大关系。当字符串大小发生变化时,创建一个新对象比在托管堆上移动对象更有效。我认为你把值/引用类型和不可变对象的概念混在一起了。

至于“==”:就像你说的,“==”是一个操作符重载,它的实现有一个很好的理由,使框架在处理字符串时更有用。

字符串是具有值语义的引用类型。这种设计是一种折衷,允许某些性能优化。

The distinction between reference types and value types are basically a performance tradeoff in the design of the language. Reference types have some overhead on construction and destruction and garbage collection, because they are created on the heap. Value types on the other hand have overhead on assignments and method calls (if the data size is larger than a pointer), because the whole object is copied in memory rather than just a pointer. Because strings can be (and typically are) much larger than the size of a pointer, they are designed as reference types. Furthermore the size of a value type must be known at compile time, which is not always the case for strings.

但是字符串有值语义,这意味着它们是不可变的,并通过值(即字符串的字符逐个字符)进行比较,而不是通过比较引用。这允许某些优化:

实习意味着如果已知多个字符串相等,编译器可以只使用一个字符串,从而节省内存。这种优化只在字符串不可变的情况下有效,否则更改一个字符串将对其他字符串产生不可预测的结果。

字符串字面值(在编译时已知)可以被编译器存储在内存的一个特殊静态区域中。这节省了运行时的时间,因为它们不需要分配和垃圾收集。

不可变字符串确实会增加某些操作的成本。例如,您不能就地替换单个字符,必须为任何更改分配一个新字符串。但与优化的好处相比,这是一个很小的成本。

值语义有效地为用户隐藏了引用类型和值类型之间的区别。如果一个类型具有值语义,那么该类型是值类型还是引用类型对用户来说并不重要——它可以被视为一个实现细节。

不仅字符串是不可变的引用类型。 还有多类型转换委托。 这就是为什么写信是安全的

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(string s)中。或者使用MemoryStreams。

如果您要创建一个库,并希望在函数中传递一个巨大的字符串,则可以将参数定义为StringBuilder或Stream。

事实上,许多人提到的堆栈和内存是关于值类型和基本类型的,因为它们必须适合微处理器中的寄存器。如果它占用的比特数超过了一个寄存器....的比特数,那么您就不能向堆栈中插入或从堆栈中取出某个东西例如,这些指令是“pop eax”——因为eax在32位系统上是32位宽的。

浮点基元类型由80位宽的FPU处理。

这早在OOP语言混淆原始类型定义之前就已经确定了,我认为值类型是专门为OOP语言创建的术语。