c#中的引用类型和值类型有什么区别?

tug*_*erk 92 .net c# value-type reference-type

几个月前有人问我这个问题,我无法详细解释.C#中的引用类型和值类型有什么区别?

我知道,价值类型int,bool,float,等和引用类型delegate,interface等等.或者,这是不对的,太?

你能以专业的方式向我解释一下吗?

Jon*_*eet 163

你的例子有点奇怪,因为虽然int,bool并且float是特定的类型,接口和委托是各种类型 - 就像structenum类型的值类型.

我已经写了一个引用类型和值类型的解释在这篇文章中.我很乐意扩展你发现令人困惑的任何一点.

"TL; DR"版本是考虑特定类型的变量/表达式的值.对于值类型,值是信息本身.对于引用类型,该值是可以为null的引用,或者可以是导航到包含该信息的对象的方式.

例如,将变量视为一张纸.它可能会写上"5"或"假"的值,但它不能拥有我的房子......它必须有我家的方向.那些方向相当于参考.特别是,两个人可能会有不同的纸张包含相同的方向到我的房子 - 如果一个人按照这些指示并将我的房子涂成红色,那么第二个人也会看到这种变化.如果他们在纸上只有我房子的单独照片,那么一个人着色他们的纸张根本不会改变另一个人的纸张.

  • 值得注意的是,事物可以提供三种不同的主要语义类型:不可变语义,可变值语义和可变引用语义.从概念上讲,事物实现的语义类型与它是作为独立堆对象还是存储为变量/字段(struct)正交.实际上,虽然不公开其字段的结构可以实现任何类型的语义,但.net允许混合共享堆引用这一事实意味着堆对象无法实现可变的值语义. (2认同)
  • @RBT:数据类型不是*仅*被分为“引用类型”和“值类型”。它们也被分为“类,结构,枚举,委托,接口”。“ int”是一个结构,“ string”是一个类,“ Action”是一个委托,依此类推。您在“ int,bool,float,类,接口,委托”中的列表是一个包含不同种类事物的列表。与“ 10,int”是包含各种事物的列表的方式相同。 (2认同)

Dur*_*n.H 23

值类型:

保留一些值而不是内存地址

例:

结构

存储:

TL; DR:变量的值存储在被删除的任何地方.例如,局部变量存在于堆栈中,但是当在一个类中作为成员声明它在堆上时,它与声明的类紧密耦合.
更长:因此值类型存储在它们声明的任何地方.例如:int作为局部变量的函数内部的值将存储在堆栈中,而int在类中声明为成员的in值将存储在具有声明的类的堆上.值类型为一个类的生命类型与声明它的类完全相同,几乎不需要垃圾收集器的工作.虽然它更复杂,我会参考@ JonSkeet的书" C#In Depth ".NET中的内存 "用于更简洁的表达式.

好处:

值类型不需要额外的垃圾回收.它将垃圾与其所在的实例一起收集.方法中的局部变量在方法离开时被清除.

缺点:

  1. 当大量值传递给方法时,接收变量实际上会复制,因此内存中有两个冗余值.

  2. 由于错过了课程,所以失去了所有的好处

参考类型:

保存值不是值的内存地址

例:

存储:

存储在堆上

好处:

  1. 将引用变量传递给方法并将其更改时,确实会更改原始值,而在值类型中,将获取给定变量的副本,并更改​​该值.

  2. 当变量的大小较大时,引用类型是好的

  3. 由于类作为引用类型变量,它们提供了可重用性,从而有利于面向对象的编程

缺点:

在读取垃圾收集器的value.extra重载时分配和解除引用时的更多工作

  • 引用类型存储在堆上并且值类型存储在堆栈中并不一定正确.如果您想了解更多信息,请阅读http://www.yoda.arachsys.com/csharp/memory.html. (5认同)

Jim*_*dra 13

如果您知道计算机如何在内存中分配内容并知道指针是什么,我发现更容易理解两者的区别.

引用通常与指针相关联.这意味着您的变量所在的内存地址实际上是在另一个内存位置中保存实际对象的另一个内存地址.

我即将给出的例子大大简化了,所以把它带上一粒盐.

想象一下,计算机存储器是连续的一堆邮政信箱(从邮政信箱0001到邮政信箱n开始)可以容纳其中的东西.如果PO框不适合您,请尝试哈希表或字典或数组或类似的东西.

因此,当您执行以下操作时:

var a ="你好";

电脑将执行以下操作:

  1. 分配存储器(例如从存储器位置1000开始5个字节)并且放置H(在1000),e(在1001),1(在1002),1(在1003)和o(在1004).
  2. 在内存中分配某些地方(例如在位置0500)并将其指定为变量a.
    所以它有点像别名(0500是a).
  3. 将该内存位置(0500)的值分配给1000(这是字符串Hello在内存中启动的位置).因此,变量a保持对"Hello"字符串的实际起始内存位置的引用.

值类型将实际内容保存在其内存位置.

因此,当您执行以下操作时:

var a = 1;

电脑将执行以下操作:

  1. 在0500分配一个内存位置并将其分配给变量a(相同的别名)
  2. 将值1放入其中(在内存位置0500).
    请注意,我们没有分配额外的内存来保存实际值(1).因此a实际上保持实际值,这就是它被称为值类型的原因.

  • @FLonLon:是的,这是一个新链接:https://learn.microsoft.com/en-us/archive/blogs/ericlippert/references-are-not-addresses (2认同)

XIV*_*ons 8

大约两年前,这是来自我的一个不同论坛的帖子.虽然语言是vb.net(而不是C#),但值类型与引用类型概念在整个.net中是统一的,并且示例仍然成立.

同样重要的是要记住,在.net中,所有类型从技术上派生自基类型Object.值类型旨在表现为这样,但最终它们还继承了基类型Object的功能.

A.值类型只是 - 它们代表存储器中存储离散VALUE的不同区域.值类型具有固定的内存大小并存储在堆栈中,堆栈是固定大小的地址的集合.

当你做出这样的陈述时:

Dim A as Integer
DIm B as Integer

A = 3
B = A 
Run Code Online (Sandbox Code Playgroud)

您已完成以下操作:

  1. 在内存中创建了2个空格,足以容纳32位整数值.
  2. 在分配给A的内存分配中放置值3
  3. 在分配给B的内存分配中将值3赋值为与A中保存的值相同的值.

每个变量的值在每个内存位置都是离散存在的.

B.参考类型可以是各种尺寸.因此,它们不能存储在"堆栈"中(记住,堆栈是固定大小的内存分配的集合吗?).它们存储在"托管堆"中.托管堆上每个项目的指针(或"引用")都保存在堆栈中(如地址).您的代码使用堆栈中的这些指针来访问存储在托管堆中的对象.因此,当您的代码使用引用变量时,它实际上使用指针(或"地址"指向托管堆中的内存位置).

假设您创建了一个名为clsPerson的类,其字符串为Property Person.Name

在这种情况下,当您发出如下声明时:

Dim p1 As clsPerson
p1 = New clsPerson
p1.Name = "Jim Morrison"

Dim p2 As Person

p2 = p1
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,p1.Name属性将返回"Jim Morrison",正如您所期望的那样.p2.Name属性也将返回"Jim Morrison",正如您所期望的那样.我相信p1和p2都代表堆栈上的不同地址.但是,现在您已为p2指定了值p1,p1和p2都指向托管堆上的SAME LOCATION.

现在COnsider这种情况:

Dim p1 As clsPerson
Dim p2 As clsPerson

p1 = New clsPerson
p1.Name = "Jim Morrison"

p2 = p1

p2.Name = "Janis Joplin"
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您已在Managed Heap上创建了一个新的Person类实例,其中Stack上的指针p1引用了该对象,并再次为该对象实例的Name属性赋值为"Jim Morrison".接下来,您在堆栈中创建了另一个指针p2,并将其指向托管堆上与p1引用的地址相同的地址(当您创建了分配p2 = p1时).

这就是扭曲.当您为p2的Assign the Name属性赋值"Janis Joplin"时,您正在更改两个p1和p2对象REFERENCED的Name属性,这样,如果您运行以下代码:

MsgBox(P1.Name)
'Will return "Janis Joplin"

MsgBox(p2.Name)
'will ALSO return "Janis Joplin"Because both variables (Pointers on the Stack) reference the SAME OBJECT in memory (an Address on the Managed Heap). 
Run Code Online (Sandbox Code Playgroud)

这有意义吗?

持续.如果你这样做:

DIm p1 As New clsPerson
Dim p2 As New clsPerson

p1.Name = "Jim Morrison"
p2.Name = "Janis Joplin"
Run Code Online (Sandbox Code Playgroud)

您现在有两个不同的人物对象.但是,你再次这样做的那一刻:

p2 = p1
Run Code Online (Sandbox Code Playgroud)

你现在已经回到了"吉姆莫里森".(我不确定p2所引用的堆上的对象发生了什么......我认为它已经超出了范围.这是有希望的人可以让我直接的那些领域之一......).-EDIT:我相信这就是为什么你要在做新的作业之前设置p2 = Nothing OR p2 =新的clsPerson.

再一次,如果你现在这样做:

p2.Name = "Jimi Hendrix"

MsgBox(p1.Name)
MsgBox(p2.Name)
Run Code Online (Sandbox Code Playgroud)

msgBoxes现在都将返回"Jimi Hendrix"

这可能会让人感到困惑,我最后一次会说,我可能有一些细节错了.

祝你好运,希望其他比我更了解的人会来帮助澄清其中的一些...


小智 5

值数据类型参考数据类型

1) (直接包含数据)但 引用 (指数据)

2)在值中(每个变量都有其自己的副本),但
引用中(超过变量可以引用某些对象)

3)在值上(操作变量不能影响其他变量),但在参考中(变量可以影响其他变量)

4) 值类型为(int,bool,float)但 引用类型为(array,class objects,string)