为了便于文章的开展,首先介绍装箱(Boxing)和拆箱(Unboxing)这两个名词。.Net的类型分为两种,一种是值类型,另一种是引用类型。这两个类型的本质区别,值类型数据是分配在栈中,而引用类型数据分配在堆上。那么如果要把一个值类型数据放到堆上,就需要装箱操作;反之,把一个放在堆上的值类型数据取出来,则需要进行拆箱操作。

  例如,对于如下简单的装箱和拆箱操作语句。

int i = 123;
  object obj = i;//Boxing
  if( obj is int )
  int j = (int) obj;//Unboxing

  为了,更好的诠释装箱和拆箱操作,我借用MSDN关于“Boxing”的解释图,具体如下。

  

  明白了这两名词的意思,现在说说为什么要减少装箱和拆箱操作。

  原因有两个,主要是关于效率:一个就是对于堆的操作效率比较低;另一个就是对于堆上分配的 内存 资源,需要GC来回收,从而降低程序效率。

  考虑到这两点因素,那么需要在程序中减少装箱和拆箱操作。

  如何减少呢,涉及到这两个操作比较多的是,格式化输出操作,例如:String.Format,Console.WriteLine之类的语句。

  例如:

Console.WriteLine( "Number list:{0}, {1}, {2}",1,2,3 );

  
  对于“1,2,3”来说,相当于前面的“123”一样,需要经过装箱和拆箱两个操作。那么如何避免呢,其实只要向WriteLine传递引用类型数据即可,也就是按照如下的方式。

Console.WriteLine( "Number list:{0}, {1}, {2}", 1.ToString(),2.ToString(),3.ToString() );

  由于“1.ToString()”的结果是String类型,属于引用类型,因此不牵扯装箱和拆箱操作。

  其次,牵扯到装箱和拆箱操作比较多的就是在集合中,例如:ArrayList或者HashTable之类。

  把值类型数据放到集合中,可能会出现潜在错误。例如:

public struct Person
{
 private string _Name;
 public string Name
 {
  get{ return _Name; }
  set{ _Name = value; }
 }
 public Person( string PersonName )
 {
  _Name = PersonName;
 }

 public override string ToString()
 {
  return _Name;
 }
}

// Using the person in a collection

ArrayList arrPersons = new ArrayList();
Person p = new Person( "OldName" );
arrPersons.Add( p );

// Try to change the name

p = ( Person ) arrPersons[0] ;
p.Name = "NewName";
Debug.WriteLine( ( (Person ) arrPersons[0] ).Name );//It's "OldName"

  这个问题其实在前面的文章中已经讲过了。有人可能会说,是否可以按照如下的方式去修改呢。

( (Person ) arrPersons[0] ).Name = "NewName";//Can't be compiled

  
  很不幸,如上操作不能通过编译。为什么呢,对于“( (Person ) arrPersons[0] )”来说,是系统用一个临时变量来接收拆箱后的值类型数据,那么由于值类型是分配在栈上,那么操作是对实体操作,可是系统不允许对一个临时值类型数据进行修改操作。

// Using the person in a collection

ArrayList arrPersons = new ArrayList();
Person p = new Person( "OldName" );
arrPersons.Add( p );

// Try to change the name

p = ( Person ) arrPersons[0] ;
p.Name = "NewName";
arrPersons.RemoveAt( 0 );//Remove old data first
arrPersons.Insert( 0, p );//Add new data
Debug.WriteLine( ( (Person ) arrPersons[0] ).Name );//It's "NewName"

  其实,这样操作会产生过多装箱和拆箱操作。那么更好的方法,可以通过接口来完成,从而减少装箱和拆箱操作。对于这个例子的接口实现应该如下。

public interface IPersonName
{
 string Name{ get;set;}
}

public struct Person:IPersonName
{
 private string _Name;
 public string Name
 {
  get{ return _Name; }
  set{ _Name = value; }
 }
 public Person( string PersonName )
 {
  _Name = PersonName;
 }

 public override string ToString()
 {
  return _Name;
 }
}

// Using the person in a collection

ArrayList arrPersons = new ArrayList();
Person p = new Person( "OldName" );

arrPersons.Add( p );
// Change the name

( (IPersonName)arrPersons[0] ).Name = "NewName";
Debug.WriteLine( ( (Person ) arrPersons[0] ).Name );//It's "NewName"

  很多人就问,为什么值类型不能修改,即

( (Person ) arrPersons[0] ).Name = "NewName";//Can't be compiled

  而如上的接口类型就能修改呢,即

( (IPersonName)arrPersons[0] ).Name = "NewName";

  这是由于产生的临时变量的类型不同,前者已经在前面进行说明了,后者由于产生的临时变量的类型为IPersonName,属于引用类型,那么相当于临时变量就是原对象的引用,那么对于对于它的修改会直接修改到原对象,因此是可以的。可以说这里的不同本身在于产生临时对象的类型不同,从而造成本质的区别。

  通过接口来改写,这样就减少了装箱和拆箱操作,同时也保证了修改的正确性。不过要注意的是,这里接口对于的是引用类型,如果接口访问的或者返回的是值类型,那么用接口虽说能实现了,但是对于装箱和拆箱操作来说,并没有减少。

  对于装箱和拆箱操作来说,基本上就讲完了,只要记住频繁装箱和拆箱操作会降低程序效率,因此在编写的时候要尽量避免。

C# 如何减少装箱拆箱相关推荐

  1. CLR via C# 中关于装箱拆箱的摘录

     装箱: 为了将一个值类型转换成一个引用类型,要使用一个名为装箱(boxing)的机制.下面总结了对值类型的一个实例进行装箱操作时在内部发生的事情. 1.在托管堆中分配好内存.分配的内存量是值类型的各 ...

  2. Java自动装箱/拆箱 - Java那些事儿

    昨天Java基本数据类型和引用类型一文中漏了几张图,已经补上,需要的自己回头去看,本系列文章首发于公众号:saysayJava. 在让人疑惑的Java代码 - Java那些事儿 一文中我们说到编译器自 ...

  3. C#装箱,拆箱和强制转换(转)

    出处:https://www.cnblogs.com/fengjiulin110120/p/6605739.html 关系: 强制转换就包含有装箱拆箱操作,装箱就是把值类型转换成引用类型,反之就是拆箱 ...

  4. .Net装箱拆箱编程实例

    .Net装箱拆箱编程实例 一 装箱是将值类型转换为引用类型 :拆箱是将引用类型转换为值类型. 利用装箱和拆箱功能,可通过允许值类型的任何值与Object 类型的值相互转换,将值类型与引用类型链接起来. ...

  5. Java基础笔记 – 增强的for循环For each循环 自动装箱拆箱 可变参数

    1.For each循环:1.1.语法:1.2.For each循环的使用:1.3.嵌套For each循环:1.4.三种循环遍历集合的列举:1.5.增强的for循环的缺点:2.自动装箱/拆箱(Aut ...

  6. 6个重要的.NET概念:栈,堆,值类型,引用类型,装箱,拆箱

    引言 本篇文章主要介绍.NET中6个重要的概念:栈,堆,值类型,引用类型,装箱,拆箱.文章开始介绍当你声明一个变量时,编译器内部发生了什么,然后介绍两个重要的概念:栈和堆:最后介绍值类型和引用类型,并 ...

  7. java(17) - 增强for循环、装箱拆箱、可变参数

    一.增强型for循环: 语法格式: 打印: A B C D E 当遍历集合或数组时,如果需要访问集合或数组的下标时,最好使用旧的方法来便利或循环,而不要用增强型for循环,因为它丢失了下标信息. 对于 ...

  8. Java八种基本数据类型的大小,以及封装类,自动装箱/拆箱的用法?

    参考:http://blog.csdn.net/mazhimazh/article/details/16799925 1. Java八种基本数据类型的大小,以及封装类,自动装箱/拆箱的用法? 原始类型 ...

  9. 反射、装箱拆箱、ArrayList与Array的区别 - 天生舞男 - 博客园

    反射.装箱拆箱.ArrayList与Array的区别 Posted on 2005-09-11 23:11 天生舞男 阅读(125) 评论(0) 编辑 收藏 引用 网摘 所属分类: SPS 1 什么是 ...

最新文章

  1. SAP、ORACLE、用友、金蝶四大ERP软件供应商的区别
  2. Vue Nuxtjs Cannot set property 'render' of undefined解决方法
  3. maven deploy plugin_Maven工程概念和关系
  4. 【存储过程】Merge Into语句实现Insert/Update在Oracle中的应用
  5. android自定义属性dimen,android代码里的dimen
  6. .Net日志之nlog
  7. PyQt5入门——QListWidget实现图片缩略图列表
  8. windows xp 创建 Oracle(11G)数据库实例时写入系统日志失败解决方案
  9. Mysql 的 Explain性能分析
  10. java 时间戳验证_关于Java:在时间戳服务器上使用时间戳和身份验证对jar进行签名...
  11. bzoj 3403: [Usaco2009 Open]Cow Line 直线上的牛
  12. Android 横竖屏切换问题
  13. 抗锯齿_像素画技巧AA手工抗锯齿教程
  14. 常见职位的英文简称_英语面试常见的50大问题及应对技巧
  15. pandas.DataFrame及xgboost代码示例
  16. 划分vlan实验心得体会_vlan划分实验报告.doc
  17. Matlab运算符总结
  18. 磁珠特性以及选型注意事项
  19. java代码计算两个时间相差的天数:
  20. Netty(四十一) - 心跳(heartbeat)源码剖析

热门文章

  1. 【CAPL】CAPL的简单介绍及变量
  2. python基于大数据的boss直聘数据招聘职位分析系统django
  3. 经典问题----倒水(详细解析)
  4. Linux DRM(二)基本概念和特性
  5. 在不停业务的情况下重启ES集群中的节点
  6. 平安居家养老服务上市
  7. 无capwap隧道的分布式网关实现思路
  8. Arduino使用HC05蓝牙模块与手机连接
  9. 人民币纸币采用防伪油墨
  10. 【NLP】文档集数据处理 gensim corpora.Dictionary 的简单使用