tug*_*erk 92 .net c# value-type reference-type
几个月前有人问我这个问题,我无法详细解释.C#中的引用类型和值类型有什么区别?
我知道,价值类型int,bool,float,等和引用类型delegate,interface等等.或者,这是不对的,太?
你能以专业的方式向我解释一下吗?
Jon*_*eet 163
你的例子有点奇怪,因为虽然int,bool并且float是特定的类型,接口和委托是各种类型 - 就像struct和enum类型的值类型.
我已经写了一个引用类型和值类型的解释在这篇文章中.我很乐意扩展你发现令人困惑的任何一点.
"TL; DR"版本是考虑特定类型的变量/表达式的值.对于值类型,值是信息本身.对于引用类型,该值是可以为null的引用,或者可以是导航到包含该信息的对象的方式.
例如,将变量视为一张纸.它可能会写上"5"或"假"的值,但它不能拥有我的房子......它必须有我家的方向.那些方向相当于参考.特别是,两个人可能会有不同的纸张包含相同的方向到我的房子 - 如果一个人按照这些指示并将我的房子涂成红色,那么第二个人也会看到这种变化.如果他们在纸上只有我房子的单独照片,那么一个人着色他们的纸张根本不会改变另一个人的纸张.
Dur*_*n.H 23
保留一些值而不是内存地址
例:
结构
存储:
TL; DR:变量的值存储在被删除的任何地方.例如,局部变量存在于堆栈中,但是当在一个类中作为成员声明它在堆上时,它与声明的类紧密耦合.
更长:因此值类型存储在它们声明的任何地方.例如:int作为局部变量的函数内部的值将存储在堆栈中,而int在类中声明为成员的in值将存储在具有声明的类的堆上.值类型为一个类的生命类型与声明它的类完全相同,几乎不需要垃圾收集器的工作.虽然它更复杂,我会参考@ JonSkeet的书" C#In Depth ".NET中的内存 "用于更简洁的表达式.
好处:
值类型不需要额外的垃圾回收.它将垃圾与其所在的实例一起收集.方法中的局部变量在方法离开时被清除.
缺点:
当大量值传递给方法时,接收变量实际上会复制,因此内存中有两个冗余值.
由于错过了课程,所以失去了所有的好处
保存值不是值的内存地址
例:
类
存储:
存储在堆上
好处:
将引用变量传递给方法并将其更改时,确实会更改原始值,而在值类型中,将获取给定变量的副本,并更改该值.
当变量的大小较大时,引用类型是好的
由于类作为引用类型变量,它们提供了可重用性,从而有利于面向对象的编程
缺点:
在读取垃圾收集器的value.extra重载时分配和解除引用时的更多工作
Jim*_*dra 13
如果您知道计算机如何在内存中分配内容并知道指针是什么,我发现更容易理解两者的区别.
引用通常与指针相关联.这意味着您的变量所在的内存地址实际上是在另一个内存位置中保存实际对象的另一个内存地址.
我即将给出的例子大大简化了,所以把它带上一粒盐.
想象一下,计算机存储器是连续的一堆邮政信箱(从邮政信箱0001到邮政信箱n开始)可以容纳其中的东西.如果PO框不适合您,请尝试哈希表或字典或数组或类似的东西.
因此,当您执行以下操作时:
var a ="你好";
电脑将执行以下操作:
值类型将实际内容保存在其内存位置.
因此,当您执行以下操作时:
var a = 1;
电脑将执行以下操作:
大约两年前,这是来自我的一个不同论坛的帖子.虽然语言是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)
您已完成以下操作:
每个变量的值在每个内存位置都是离散存在的.
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)