一、值类型和引用类型的基础知识
值类型和引用类型的区别在于变量的值在它声明时的存储位置。局部变量的值总是存储在栈(stack)中,实例变量的值总是存储在实例本身存储的地方。引用类型实例(对象)总是存储在堆(heap)中,静态变量也是在堆中。
两种类型的另一个差异在于,值类型不可以派生出其他类型。这将导致的一个结果就是,值不需要额外的信息来描述值实际是什么类型。把它同引用类型比较,对于引用类型来说,每个对象的开头都包含一个数据块,它标识了对象的实际数据,同时还提供了其他一些信息。
引用本身并不知道对象的类型-所以同一个引用“值”可用于(引用)不同类型的多个变量。如下:
Stream stream = new MemoryStream();
MemoryStream memoryStream = (MemoryStream) stream;
第1行创建的一个新的MemoryStream对象,将stream变量的值设为对那个新对象的引用。
第2行检查stream的值引用的是不是一个MemoryStream(或派生类型)对象,并将MemoryStream的值设为相同的值。
二、值类型和引用类型的误区
误区1. “结构是轻量级的类”
有人认为值类型不能或不应有方法或其他有意义的行为 - 他们应作为简单的数据转移类型来使用,只应该有public字段或简单的属性。
对于这个说法,一个非常典型的反例就是DateTime类型:它作为值类型来提供是很有道理的,因为它非常适合作为和数字或字符相似的一个基本单位来使用。另外,它也理应被赋予对它的值执行计算的能力。
误区2. “引用类型保存在堆上,值类型保存在栈上”
第一部分是正确的--引用类型的实例总是在堆上创建的。但第二部分就有问题了。变量的值是在它声明的位置存储的。所以,假定一个类中有一个int类型的实例变量,那么在这个类的任何对象中,该变量的值总是和对象中的其他数据在一起,也就是在堆上。只有局部变量(方法内部声明的变量)和方法参数在栈上。对于c#2及以上版本,很多局部变量并不完全存放在栈上。
误区3. “对象在C#中默认是通过引用传递的”
无论是引用传递还是值传递,永远不会传递对象本身。涉及一个引用类型时,要么以“引用传递”的方式传递变量,要么以“传值”的方式传递参数值(引用)。
三、装箱和拆箱
int i = 5;
object 0 = i;
int j = (int) o;
i是值类型变量,0是引用类型变量。o的值是一个引用,而i不是引用,它是一个整数值。实际发生的事情就是装箱:运行时将在堆上创建一个包含值(5)的对象(它是一个普通对象)。o的值是对该对象的一个引用。该对象的值是原始值的一个副本,改变i的值不会改变箱内的值。同样拆箱也会复制箱内的值,在赋值之后,j和对象之间不再有任何关系。
之所以要留意装箱和拆箱,是由于他们可能会降低性能。多次装箱拆箱会创建众多对象,而这些对象会加重垃圾回收期的负担。
摘选自《深入理解C# (第3版)》第2章
本文作者:wwmin
微信公众号: DotNet技术说
本文链接:https://www.jianshu.com/p/c20686834877
关于博主:评论和私信会在第一时间回复。或者[直接私信]我。
版权声明:转载请注明出处!
声援博主:如果您觉得文章对您有帮助,关注点赞, 您的鼓励是博主的最大动力!
网友评论