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. .NET面试题解析(02)-拆箱与装箱

      系列文章目录地址: .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引 装箱和拆箱几乎是所有面试题中必考之一,看上去简单,就往往容易被忽视.其实它一点都不简单的,一个简单的问题也 ...

  2. JAVA进阶之旅(一)——增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法

    JAVA进阶之旅(一)--增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法 学完我们的java之旅,其实收获还是很多的,但是依然还有很 ...

  3. Java知多少(24)包装类、拆箱和装箱详解

    虽然 Java 语言是典型的面向对象编程语言,但其中的八种基本数据类型并不支持面向对象编程,基本类型的数据不具备“对象”的特性——不携带属性.没有方法可调用. 沿用它们只是为了迎合人类根深蒂固的习惯, ...

  4. JAVA中拆箱和装箱

    浅谈JAVA中拆箱与装箱 一.  什么是装箱?什么是拆箱? 在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行: Integer i = new Integer(10) ...

  5. C# 拆箱与装箱及优化

    1.概念 装箱在值类型向引用类型转换时发生,在堆中分配. 拆箱在引用类型向值类型转换时发生. 2.装箱拆箱的过程 //这行语句将整型常量1赋给object类型的变量obj:众所周知常量1是值类型,值类 ...

  6. [Java学习] Java包装类、拆箱和装箱详解

    虽然 Java 语言是典型的面向对象编程语言,但其中的八种基本数据类型并不支持面向对象编程,基本类型的数据不具备“对象”的特性——不携带属性.没有方法可调用. 沿用它们只是为了迎合人类根深蒂固的习惯, ...

  7. Java包装类、拆箱和装箱详解

    转载:https://www.cnblogs.com/ok932343846/p/6749488.html 虽然 Java 语言是典型的面向对象编程语言,但其中的八种基本数据类型并不支持面向对象编程, ...

  8. int和Integer及拆箱与装箱

    int和Integer 如果面试官问Integer与int的区别:估计大多数人只会说道两点,Ingeter是int的包装类,int的初值为0,Ingeter的初值为null.但是如果面试官再问一下In ...

  9. 《Java基础知识》Java包装类,拆箱和装箱

    虽然 Java 语言是典型的面向对象编程语言,但其中的八种基本数据类型并不支持面向对象编程,基本类型的数据不具备“对象”的特性——不携带属性.没有方法可调用. 沿用它们只是为了迎合人类根深蒂固的习惯, ...

随机推荐

  1. hiho 第1周 最长回文子串

    题目链接:http://hihocoder.com/problemset/problem/1032 #include <bits/stdc++.h> using namespace std ...

  2. Poj(3686),最小权匹配,多重匹配,KM

    题目链接 The Windy's | Time Limit: 5000MS | Memory Limit: 65536K | | Total Submissions: 4939 | Accepted: ...

  3. 2016年12月4日 星期日 --出埃及记 Exodus 20:25

    2016年12月4日 星期日 --出埃及记 Exodus 20:25 If you make an altar of stones for me, do not build it with dress ...

  4. 2016年10月28日 星期五 --出埃及记 Exodus 19:13

    2016年10月28日 星期五 --出埃及记 Exodus 19:13 He shall surely be stoned or shot with arrows; not a hand is to ...

  5. linux Centos 6.5 FTP服务原理及vsfptd的安装、配置(转)

    本篇随笔将讲解FTP服务的原理以及vsfptd这个最常用的FTP服务程序的安装与配置... 一.FTP服务原理 FTP(File Transfer Protocol)是一个非常古老并且应用十分广泛的文 ...

  6. SpringMVC拦截器2(资源和权限管理)(作为补充说明)

    SpringMVC拦截器(资源和权限管理) 1.DispatcherServlet SpringMVC具有统一的入口DispatcherServlet,所有的请求都通过DispatcherServle ...

  7. JQuery增删改查

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  8. Linux常用命令大杂烩(持续更新)

    1.vimn,$s/findstr/targetstr/g #替换n到文档末尾的所有字符串:% s/^.\{4\}//g #将当前缓冲区的所有行的前4个字符删除 2.每周日早上3:30删除日志30 3 ...

  9. NSKeyedArchive(存储自定义对象)

    在viewController.m中: - (void)viewDidLoad { [super viewDidLoad]; ZWPerson *p = [[ZWPerson alloc] init] ...

  10. hdu 栈题1022&1702

    http://acm.hdu.edu.cn/showproblem.php?pid=1022 http://blog.csdn.net/swm8023/article/details/6902426此 ...