为什么字符串是引用类型?

时间:2010-09-07 05:23:05

标签: c# string primitive-types reference-type

为什么string是引用类型,即使它通常是原始数据类型,如int,float或double。

3 个答案:

答案 0 :(得分:18)

除了Dan发布的原因外:

值类型是按照定义那些将值存储在自身中的类型,而不是引用到其他地方的值。这就是为什么值类型被称为“值类型”,而引用类型被称为“引用类型”。所以你的问题实际上是“为什么字符串引用其内容而不是简单地包含其内容?”

这是因为值类型具有nice属性,给定值类型的每个实例在内存中具有相同的大小。

那又怎样?为什么这是一个不错的财产?好吧,假设字符串是可以是任何大小的值类型,请考虑以下内容:

string[] mystrings = new string[3];

三个字符串数组的初始内容是什么?值类型没有“null”,因此唯一明智的做法是创建一个包含三个空字符串的数组。怎么会在记忆中被列出来?想一想。你会怎么做?

现在假设你说

string[] mystrings = new string[3];
mystrings[1] = "hello";

现在我们在数组中有“”,“hello”和“”。 在内存中“hello”的位置是什么?无论如何,为mystrings [1]分配的插槽有多大?数组及其元素的内存必须某处

这使CLR有以下选择:

    每次更改其中一个元素时,
  • 调整数组,复制整个内容,大小可能是兆字节
  • 禁止创建未知大小的值类型数组
  • 禁止创建未知大小的值类型

CLR团队选择了后者。 将字符串设置为引用类型意味着您可以有效地创建它们的数组。

答案 1 :(得分:10)

Yikes,这个答案被接受,然后我改变了它。我应该在底部包含原始答案,因为那是OP所接受的。

新答案

更新:这就是问题所在。 string 绝对需要表现为引用类型。到目前为止,所有答案都触及了这个原因:string类型没有常量大小,将字符串的全部内容从一个方法复制到另一个方法是没有意义的,{{1}否则,数组必须调整主题大小 - 仅举几例。

但您仍然可以定义 string[]作为string,内部指向struct数组甚至是char[]指针和{ {1}}为了它的长度,让它变成不可变的,并且瞧!,你有一个类型,的行为类似于一个引用类型,但技术上 em>值类型。

老实说,这看起来很傻。正如Eric Lippert在其他答案的一些评论中指出的那样,定义这样的值类型与定义引用类型基本相同。几乎在所有意义上,它都与以相同方式定义的引用类型无法区分。

所以问题的回答“为什么char*是引用类型?”基本上是:“让它成为一种价值类型只会是愚蠢的。”但如果那只是唯一的原因,那么真正的逻辑结论是int实际上可以被定义为如上所述的string并且没有特别好的论据反对那种选择。

然而,的原因,最好让string成为struct,而不是string,而不仅仅是纯粹的知识分子。这是我能想到的一对夫妇:

防止拳击

如果class是一个值类型,那么每次将它传递给期望struct的某个方法时,都必须装箱,这会创建一个新的string,膨胀堆并导致无意义的GC压力。由于字符串基本上无处不在,因此让它们一直引起装箱将是一个大问题。

用于直观的相等比较

是的,object可以覆盖object,无论它是引用类型还是值类型。但是如果是值类型,那么string将返回 false 这是因为两个参数都会被装箱,并且盒装参数永远不会有相同的参考(据我所知)。

所以,即使你可以定义一个值类型来执行就像引用类型一样,它由一个引用类型字段组成,但它仍然不是 一样。所以我认为这是Equals作为引用类型的更完整的原因:你可以使它成为一个值类型,但这只会给它带来不必要的弱点。


原始答案

它是一种引用类型,因为只传递引用

如果是值类型,则每次将字符串从一个方法传递到另一个方法时,整个字符串都将被复制*。

因为它是一个引用类型,而不是像“Hello world!”这样的字符串值。传递 - “你好世界!”顺便说一下,它是12个字符,这意味着它需要(至少)24个字节的存储空间 - 只传递引用来传递这些字符串。传递引用比传递字符串中的每个字符要便宜得多。

此外,它实际上不是正常的原始数据类型。是谁告诉你的?

*实际上,这并不严格。如果字符串内部持有ReferenceEquals("a", "a")数组,那么只要数组类型是引用类型,字符串的内容实际上将按值传递 - 仅引用数组将是。不过,我仍然认为这基本上是正确答案。

答案 2 :(得分:1)

String是引用类型,而不是值类型。在许多情况下,您知道字符串的长度和字符串的内容,在这种情况下,很容易为字符串分配内存。但请考虑这样的事情。

string s = Console.ReadLine();

是否无法在编译时知道“s”的分配细节。用户输入值,所有输入的字符串/行都存储在s中。因此,字符串存储在堆上,以便重新分配内存以适合字符串s的内容。并且对此字符串的引用存储在堆栈中。

要了解更多信息,请阅读:petzold的.net零

读取:从CLR通过C#收集垃圾以获取堆栈上的分配详细信息。

编辑:Console.WriteLine();到Console.ReadLine();