目前在看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. oracle数据库查询时间sql

    select * from cc_picture_info where PICTURE_SOURCE = 3 AND UPLOAD_TIME > to_date('2017-03-29 16:5 ...

  2. 点击input选中文本

    选文本: $(".unline-ipt").click(function () { $(this).focus().select(); this.selectionStart = ...

  3. 《从零开始学Swift》学习笔记(Day 10)——运算符是“ +、-、*、/ ”吗?

    原创文章,欢迎转载.转载请注明:关东升的博客 运算符是用于执行程序代码运算,会针对一个或一个以上操作数项目来进行运算.例如:2+3,其操作数是2和3,而运算符则是“+”.那么“+.-.*./”是运算符 ...

  4. Servlet3.0文件上传

    Servelt3.0文件上传作为一种便捷的文件上传方式很是值得我们去应用的 1.Servlet3.0文件上传使用步骤 浏览器端的要求 表单的提交方法必须是post 必须有一个文件上传组件 <in ...

  5. 160315、mybatis批量删除

    <deleteid="deleteCTQ" parameterType="java.lang.String"> DELETE FROM sqm_pr ...

  6. vsftp or pureftpd

    一.安装pureftpd //这个软件比vsftp配置起来更加灵活和安全. /*官网是 http://www.pureftpd.org/project/pure-ftpd*/ [root@localh ...

  7. js 一些基础知识

    数据类型: 作用域 每个函数都有自己的执行环境,执行环境定义了变量有权访问的其他数据,决定了他们各自的行为. 每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有 ...

  8. C# 构建动态Lambda表达式

    做CURD开发的过程中,通常都会需要GetList,然而查询条件是一个可能变化的需求,如何从容对应需求变化呢? 首先,我们来设计一个套路,尝试以最小的工作量完成一次查询条件的需求变更 1.UI收集查询 ...

  9. cocoapod 最新安装使用步骤

    cocoapod 最新安装使用步骤 安装 1.sudo gem update (2个-)system :更新你的gem system至最新 2.gem sources  (2个-)remove htt ...

  10. 拼团商品列表页 分析 js代码行位置对执行的影响和window.onload的原理 setTimeout传参

    w TypeError : Cannot set property 'innerHTML' of nullTypeError : Cannot set property 'value' of null ...