为什么String是值类型,虽然它是一个类而不是结构?

时间:2011-09-02 12:24:55

标签: c# string types reference value-type

采用以下示例:

string me = "Ibraheem";
string copy = me;
me = "Empty";
Console.WriteLine(me);
Console.WriteLine(copy);

输出结果为:

Empty
Ibraheem

由于它是类类型(即不是结构),因此字符串copy也应包含Empty,因为C#中的= operator指定对象的引用而不是对象本身(如C ++)...

8 个答案:

答案 0 :(得分:18)

虽然接受的答案解决了这个问题(和其他人一样),但我想给出一个专门针对你实际问的内容的答案,这是关于变量赋值的语义。

C#中的变量只是用于保存单个值的内存块。重要的是要注意,没有“值变量”和“引用变量”这样的东西,因为变量只保存值。

“值”和“参考”之间的区别来自类型。值类型(VT)表示整个数据存储在变量中。

如果我有一个名为abc的整数变量保存值100,那么这意味着我的应用程序中有一个四字节的内存块,用于存储字面值{{1} } 在里面。这是因为100是一个值类型,因此所有数据都存储在变量中。

另一方面,如果我有一个名为int的{​​{1}}变量保存值string,则涉及两个实际的内存位置。第一个是存储实际字符foo的内存块,以及有关我的字符串(其长度等)的其他信息。然后,将此位置的引用存储在我的变量中。引用与C / C ++中的指针非常相似;虽然它们不一样,但这种解释足以说明这一点。

因此,总结一下,引用类型的值是引用到内存中的另一个位置,其中值类型的值是数据本身

当您为变量分配内容时,您所要改变的只是变量的。如果我有这个:

"Adam"

然后我有两个包含相同值的字符串变量(在这种情况下,它们各自包含对同一个字符串"Adam"的引用。)如果那样做:

string str1 = "foo";
string str2 = str1;

然后我将"foo"的值更改为对字符串str1 = "bar"; 的引用。这根本不会更改str1,因为它的值仍然是对字符串"bar"的引用。

答案 1 :(得分:9)

System.String不是值类型。它展示了一些类似于值类型的行为,但您遇到的行为不是其中之一。请考虑以下代码。

class Foo 
{ 
     public string SomeProperty { get; private set; }
     public Foo(string bar) { SomeProperty = bar } 
}

Foo someOtherFoo = new Foo("B");
Foo foo = someOtherFoo;
someOtherFoo = new Foo("C");

如果您检查了foo.SomeProperty的输出,您认为它与someOtherFoo.SomeProperty相同吗?如果是这样,你对语言的理解有缺陷。

在您的示例中,您为字符串指定了值。而已。它与值类型,引用类型,类或结构无关。这是一个简单的任务,无论你是在谈论字符串,长篇还是Foos,都是如此。您的变量暂时包含相同的值(对字符串“Ibraheem”的引用),但之后您重新分配了其中一个。这些变量并非始终存在着千丝万缕的联系,它们只是暂时有一些共同点。

答案 2 :(得分:8)

它不是值类型。当您使用字符串文字时,它实际上是编译时存储的引用。因此,当您分配字符串时,您基本上就像在C ++中一样更改指针。

答案 3 :(得分:6)

字符串的行为与任何其他类相同。考虑:

class Test {
    public int SomeValue { get; set; }
    public Test(int someValue) { this.SomeValue = someValue; }
}

Test x = new Test(42);
Test y = x;
x = new Test(23);
Console.WriteLine(x.SomeValue + " " + y.SomeValue);

输出:

23 42

- 与string示例中的行为完全相同。

答案 4 :(得分:3)

您的示例显示的是字符串为的引用类型的经典行为。

答案 5 :(得分:2)

string copy = me;表示copy引用将指向me所指向的相同内存位置。

以后me可以指向其他内存位置,但不会影响copy

答案 6 :(得分:2)

如果您也使用了值类型,那么您的代码也会这样做。考虑使用整数:

int me = 1;
int copy = me;
me = 2;
Console.WriteLine(me);
Console.WriteLine(copy);

这将打印出以下内容:

2
1

答案 7 :(得分:0)

虽然其他答案确切地说明了您的答案的解决方案,但是为了更好地基本了解您为什么要读取堆和堆栈内存分配以及何时通过垃圾收集器从内存中删除数据

这是一个描述堆栈和堆内存以及垃圾收集器的好页面。在文章的底部有链接到解释的其他部分: http://www.c-sharpcorner.com/UploadFile/rmcochran/csharp_memory01122006130034PM/csharp_memory.aspx?ArticleID=9adb0e3c-b3f6-40b5-98b5-413b6d348b91

希望这可以让你更好地理解为什么