目前在看CLR via C#,把总结的记下来,索性就把他写成一个系列吧。

1.【.Net基础一】 类型、对象、线程栈、托管堆运行时的相互关系

2.【.Net基础二】浅谈引用类型、值类型和装箱、拆箱


引用类型和值类型

**引用类型:分配在托管堆中。引用类型直接继承自System.Object。引用类型可以被继承。 **

常用的引用类型有:数组、 类、 接口、 委托、 字符串

**值类型:可分配在线程栈和托管堆中。所有值类型都分为结构或枚举。值类型直接继承自System.ValueType。值类型不可以被继承。 **

例如:System.Int32结构、System.Boolean结构、System.Decimal结构、System.Nullable结构、System.DayOfWeek枚举、System.IO.FileAttributes枚举等都是值类型。

枚举属于值类型,枚举继承自System.Enum,但System.Enum由于特殊性,并不属于值类型,System.Enum属于引用类型。System.Enum继承自System.ValueType。System.ValueType和System.Enum一样,都是属于引用类型,只是CLR对继承自System.ValueType和System.Enum做了特殊处理,内存分配使他具有值类型的特性。编译器也做了特殊处理,例如不能直接继承自这些类。

结构可能我们使用的不多,这里我们可以把它当作一个特殊的“类”。详细的可以参照园子里C#之结构

还有要说明的就是int,double等这些关键字是基元类型,编译器负责将基元类型翻译成对应的FCl(Framework Class Library)中的类型。

来看个例子

   class SomeReference { public Int32 x;}//应用类型
struct SomeValue { public Int32 x;}//值类型 static void Main(string[] args)
{
/**第一部分**/
SomeReference re1 = new SomeReference();//在堆上分配
SomeValue va1 = new SomeValue();//在栈上分配
re1.x = 5;
va1.x = 5;
Console.WriteLine(re1.x);//输出5
Console.WriteLine(va1.x);//输出5 /**第二部分**/
SomeReference re2 = re1;//只复制引用(指针)
SomeValue va2 = va1;//在栈上分配并复制成员
re1.x = 8;//re1和re2都会更改
va1.x = 9;//va1会更改,va2不会
Console.WriteLine(re1.x);//输出8
Console.WriteLine(re2.x);//输出8
Console.WriteLine(va1.x);//输出9
Console.WriteLine(va2.x);//输出5
}

第一部分执行完(上一节讲到变量是在序幕代码中进行初始化到栈中):

全部执行完:


装箱和拆箱

这里将一个苹果比喻成值类型,见到苹果能知道他就是吃的苹果,苹果有多种品种(结构和枚举)。

把一个纸箱比喻成引用类型,纸箱可以装各种各样的东西(各种类)。

装箱:将值类型转换为一个引用类型

把苹果装进纸箱(在内存中是复制),就是装箱。装箱运输的话,苹果要仔细打包,很小心。

装箱步骤

  1. 在托管堆上分配内存,内存大小为值类型所有字段的大小和类型对象指针加上同步块索引的大小。
  2. 将值类型的所有字段复制到刚分配的内存中。
  3. 返回刚分配的内存地址

拆箱:将引用类型转换成值类型

拆箱可能就直接用小刀划开胶带,然后看见苹果(这里已经完成了拆箱),接着取出苹果(在内存中是复制)。

拆箱不是直接把装箱过程倒过来,拆箱的代价要比装箱低的多。拆箱其实就是一个获取指针的过程。不要求在内存中复制任何字节。

拆箱步骤

  1. 获取引用类型所有字段对应的地址,这里拆箱已经完成了。
  2. 拆箱操作完成后,会发生一次字段的复制。

来看个例子

   static void Main(string[] args)
{
Int32 v = 5;//值类型
Object o = v;//将值类型转换成应用类型,o引用一个复制的v(已装箱);
v = 123; //将未装箱的值修改成123 Console.WriteLine(v + "," + (Int32)o);//显示123,5
//这里发生了几次装箱?可能大家会看成2次。我刚开始也以为是。
Console.ReadLine();
}

这里容易发生错误的地方就是Console.WriteLine()方法。

正确的装箱次数是3次。

1.Object o = v; 将v转换成o。这是一次。

2.(Int32)o 这涉及到一次拆箱,将o转成Int32,然后再装箱成String?(真的是String吗?那第三次装箱又在哪?)

3.让我们通过ildasm.exe打开程序集,查看IL代码(书中解释的比较详细,我只说关键的地方)。

通过box关键字我们就能看到发生了三次装箱,一次拆箱。

多的一次装箱就发生在Contact方法上。这个Contact方法就是为什么多了一次装箱的关键,三个Object参数,所以对o进行拆箱完装箱其实是Int32转成String

多次装箱拆箱会影响程序的性能和内存消耗,这里不明显,如果在循环中就会产生严重的性能问题。

这里我们可以这样调用

   Console.WriteLine(v.ToString() + "," + o);//这里同样显示123,5

v.ToString()方法会返回一个String,String对象已经是引用类型,所以直接传递给Contact方法,不需要任何装箱操作。

这里调用的是String.ValueType(引用类型)的ToString()方法,所以不会发生装箱操作。

o已经指向一个Object引用类型,所以直接传递引用地址就可以。同样不需要进行装箱。

基础很重要,只有在切实理解这些概念之后,才能保证自己长期成功。只有深刻理解之后,才能更快、更轻松的构建高效率应用程序。

转载请注明出处:http://www.cnblogs.com/xcong/p/4066195.html

【.Net基础二】浅谈引用类型、值类型和装箱、拆箱的更多相关文章

  1. NET中的类型和装箱/拆箱原理

    谈到装箱拆箱,DebugLZQ相信给位园子里的博友一定可以娓娓道来,大概的意思就是值类型和引用类型的相互转换呗---值类型到引用类型叫装箱,反之则叫拆箱.这当然没有问题,可是你只知道这么多,那么Deb ...

  2. java基础1.5版后新特性 自动装箱拆箱 Date SimpleDateFormat Calendar.getInstance()获得一个日历对象 抽象不要生成对象 get set add System.arrayCopy()用于集合等的扩容

    8种基本数据类型的8种包装类 byte Byte short Short int Integer long Long float Float double Double char Character ...

  3. js包装类型的装箱拆箱

    https://www.jb51.net/article/155820.htm https://juejin.im/post/5cbaf130518825325050fb0a https://juej ...

  4. c#基础系列1---深入理解值类型和引用类型

    "大菜":源于自己刚踏入猿途混沌拾起,自我感觉不是一般的菜,因而得名"大菜",于自身共勉. 不知不觉已经踏入坑已10余年之多,对于c#多多少少有一点自己的认识, ...

  5. C#基础知识1-深入理解值类型和引用类型

    C#值类型和引用类型这个概念在刚学习的时候应该就知道了.但是我们并没有深入的去理解它.越是基础知识其实才是最有用的.对代码的优化,代码质量的提升都有帮助.通过整理本文章,对很多知识也起到了巩固的作用吧 ...

  6. 【深入理解CLR】2:细谈值类型的装箱和拆箱

    装箱 总所周知,值类型是比引用类型更“轻型”的一种类型,因为它们不作为对象在托管堆中分配,不会被垃圾回收,也不通过指针来引用.但在许多情况下,都需要获取对值类型的一个实例的引用.例如,假定要创建一个A ...

  7. [CLR via C#]5.3 值类型的装箱和拆箱

    原文:[CLR via C#]5.3 值类型的装箱和拆箱 在CLR中为了将一个值类型转换成一个引用类型,要使用一个名为装箱的机制. 下面总结了对值类型的一个实例进行装箱操作时内部发生的事: 1)在托管 ...

  8. Windows Phone 开发起步之旅之二 C#中的值类型和引用类型

    今天和大家分享下本人也说不清楚的一个C#基础知识,我说不清楚,所以我才想把它总结一下,以帮助我自己理解这个知识上的盲点,顺便也和同我一样不是很清楚的人一起学习下.  一说起来C#中的数据类型有哪些,大 ...

  9. C#学习笔记(基础知识回顾)之值类型与引用类型转换(装箱和拆箱)

    一:值类型和引用类型的含义参考前一篇文章 C#学习笔记(基础知识回顾)之值类型和引用类型 1.1,C#数据类型分为在栈上分配内存的值类型和在托管堆上分配内存的引用类型.如果int只不过是栈上的一个4字 ...

随机推荐

  1. UILabel 行间距设置

    NSMutableAttributedString * attributedString1 = [[NSMutableAttributedString alloc] initWithString:te ...

  2. Math.max得到数组中最大值

    Math.max(param1,param2) 因为参数不支持数组. 所以可以根据apply的特点来解决, var max = Math.max.apply(null,array),这样就可以轻易的得 ...

  3. sql语句判断身份证性别等

    SELECT t.card_number ,) AS "省份", SUBSTR(t.card_number,,) "出生年月", SUBSTR(t.card_n ...

  4. 使用Fastjson生成Json字符串少字段属性(数据丢失)

    最后是控制台打印生成的结果如下:FastJson生成字符串是:{"id":"2","name":"节点1"," ...

  5. LeetCode 笔记系列15 Set Matrix Zeroes [稍微有一点hack]

    题目:Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place. Fol ...

  6. centos免密登录

    本文是为了docker-machine增加现有虚拟机服务器为节点而做 docker-machine create -d generic --generic-ip-address=192.168.102 ...

  7. 在Mac下使用ll,la,l

    一: 用户目录下建立一个脚本“.bash_profile”,并输入以下内容即可: alias ll='ls -alF' alias la='ls -A' alias l='ls -CF' 二: sou ...

  8. How to Design a Good API and Why it Matters

    前谷歌首席 Java 架构师谈如何设优秀的 API – 码农网 http://www.codeceo.com/article/google-java-good-api.html 2015-11-24 ...

  9. jquery的常用知识点

    一.用jquery寻找元素 1.选择器 基本选择器: $("*") $("#id") 用id匹配 $(".class") 用class名匹配 ...

  10. cpython解释器内存机制

    java虚拟机内存 笼统分为两部分:堆区,栈区 其中,引用在栈区,对象在堆区 详细分为五部分:堆区,虚拟机栈区,本地方法栈区,方法区,程序计数器 cpython解释器内存 笼统分为两部分:堆区,栈区 ...