《Effective C#》之减少装箱和拆箱_天极网 http://dev.yesky.com/msdn/359/3486359.shtml

《Effective C#》之减少装箱和拆箱

2007-08-12 11:00  作者:愚翁
  为了便于文章的开展,首先介绍装箱(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,属于引用类型,那么相当于临时变量就是原对象的引用,那么对于对于它的修改会直接修改到原对象,因此是可以的。可以说这里的不同本身在于产生临时对象的类型不同,从而造成本质的区别。

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

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

https://tig.qpic.cn/doc/2018腾讯移动游戏技术评审标准与实践案例.pdf

Struct和Class的基本情况大致已经清楚,我们的真正目的是想知道如何架构我们的数据操作模块,才能充分地利用值类型和引用类型的,让效率最高(即装箱或拆线少,堆内存少)。

字符串拼接

数据结论

Struct在栈中不产生GC,class在堆中,会产生GC。对Struct的结点修改时,修改完以后记得重新赋值。因为Struct赋值是copy而不是引用,修改完以后,以前的不生效。

意义

堆栈的空间有限,对于大量的逻辑的对象,创建类要比创建结构好一些。结构表示轻量对象,并且结构的成本较低,适合处理大量短暂的对象。在表现抽象和多级别的对象层次时,类是最好的选择。大多数情况下该类型只是一些数据时,结构是最佳的选择。

C# | Boxing And Unboxing - GeeksforGeeks https://www.geeksforgeeks.org/c-sharp-boxing-unboxing/

// C# implementation to demonstrate
// the Boxing
using System;
class GFG {
  
    // Main Method
    static public void Main()
    {
  
        // assigned int value
        // 2020 to num
        int num = 2020;
  
        // boxing
        object obj = num;
  
        // value of num to be change
        num = 100;
  
        System.Console.WriteLine
        ("Value - type value of num is : {0}", num);
        System.Console.WriteLine
        ("Object - type value of obj is : {0}", obj);
    }

 

Value - type value of num is : 100
Object - type value of obj is : 2020

// C# implementation to demonstrate
// the Unboxing
using System;
class GFG {
  
    // Main Method
    static public void Main()
    {
  
        // assigned int value
        // 23 to num
        int num = 23;
  
        // boxing
        object obj = num;
  
        // unboxing
        int i = (int)obj;
  
        // Display result
        Console.WriteLine("Value of ob object is : " + obj);
        Console.WriteLine("Value of i is : " + i);
    }
}
  • Value of ob object is : 23
    Value of i is : 23
 
 
 


 
 
 
 
 
 
 
 

《Effective C#》之减少装箱和拆箱的更多相关文章

  1. C# 程序性能提升篇-1、装箱和拆箱,枚举的ToString浅析

    前景提要: 编写程序时,也许你不经意间,就不知不觉的使程序代码,发生了装箱和拆箱,从而降低了效率,不要说就发生那么一次两次,如果说是程序中发生了循环.网络程序(不断请求处理的)等这些时候,减少装箱和拆 ...

  2. Java暗箱操作之自动装箱与拆箱

    我以前在写Android项目的时候,估计写得最多最熟练的几句话就是: List<Integer> list = new ArrayList<Integer>(); list.a ...

  3. C#基础知识系列二(值类型和引用类型、可空类型、堆和栈、装箱和拆箱)

    前言 之前对几个没什么理解,只是简单的用过可空类型,也是知道怎么用,至于为什么,还真不太清楚,通过整理本文章学到了很多知识,也许对于以后的各种代码优化都有好处. 本文的重点就是:值类型直接存储其值,引 ...

  4. C# 知识回顾 - 装箱与拆箱

    装箱与拆箱 目录 生活中的装箱与拆箱 C# 的装箱与拆箱 值类型和引用类型 装箱 拆箱 生活中的装箱与拆箱    我们习惯了在网上购物,这次你想买本编程书 -- <C 语言从入门到放弃> ...

  5. [C#] C# 知识回顾 - 装箱与拆箱

    装箱与拆箱 目录 生活中的装箱与拆箱 C# 的装箱与拆箱 值类型和引用类型 装箱 拆箱 读者见解 生活中的装箱与拆箱    我们习惯了在网上购物,这次你想买本编程书 -- <C 语言从入门到放弃 ...

  6. 深入理解C#的装箱和拆箱(转)

    装箱和拆箱是值类型和引用类型之间相互转换是要执行的操作.  1. 装箱在值类型向引用类型转换时发生 2. 拆箱在引用类型向值类型转换时发生 光上述两句话不难理解,但是往深处了解,就需要一些篇幅来解释了 ...

  7. 转载:详解Java 自动装箱与拆箱的实现原理

    原文:http://www.jb51.net/article/111847.htm 什么是自动装箱和拆箱 自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对 ...

  8. CLR via 笔记 5.3 值类型的装箱和拆箱

    1.装箱 为了将一个值类型转换成一个引用类型,要使用一个名为装箱(Boxing)的机制. 1.在托管堆中分配好内存.分配的内存量是值类型的各个字段需要的内存量加上托管堆的所有对象都有的两个额外成员(类 ...

  9. [C#] 类型学习笔记一:CLR中的类型,装箱和拆箱

    在学习.NET的时候,因为一些疑问,让我打算把.NET的类型篇做一个总结.总结以三篇博文的形式呈现. 这篇博文,作为三篇博文的第一篇,主要探讨了.NET Framework中的基本类型,以及这些类型一 ...

随机推荐

  1. python初学者-判断今天是今年的第几天代码

    判断今天是今年的第几天源代码 import time date =time.localtime() year,month,day=date[:3] day_month=[31,28,31,30,31, ...

  2. three.js WebGLRenderTarget

    今天郭先生说一说WebGLRenderTarget,它是一个缓冲,就是在这个缓冲中,视频卡为正在后台渲染的场景绘制像素. 它用于不同的效果,例如把它做为贴图使用或者图像后期处理.线案例请点击博客原文. ...

  3. java数组之system.arrayCopy

    public class ArrayDemo { /* public static void main(String[] args) { int[] a=new int[4]; int[] b=new ...

  4. C++ string的内部究竟是什么样的?

    在C语言中,有两种方式表示字符串: 一种是用字符数组来容纳字符串,例如char str[10] = "abc",这样的字符串是可读写的: 一种是使用字符串常量,例如char *st ...

  5. 将WCF迁移到gRPC

    使用protobuf-net.Grpc将WCF服务迁移到gRPC非常简单.在这篇博文中,我们将看看它到底有多简单.微软关于将WCF服务迁移到gRPC的官方指南只提到了Gooogle.Protobuf方 ...

  6. 2021升级版微服务教程—为什么会有微服务?什么是SpringCloud?

    2021升级版SpringCloud教程从入门到实战精通「H版&alibaba&链路追踪&日志&事务&锁」 教程全目录「含视频」:https://gitee.c ...

  7. hive行转列,列转行

    实例一:来源: https://www.cnblogs.com/kimbo/p/6208973.html 行转列 (对某列拆分,一列拆多行) 使用函数:lateral view explode(spl ...

  8. apply 和 call的用法、区别

    1.JavaScript中函数是对象的方法,如果一个函数不是js对象的方法那一定是全局对象的函数,每个函数的对象都有apply和call方法,即每个对象都有call and apply apply:调 ...

  9. (解决)easypoi模板导出多个excel文件并压缩

    目录 easypoi版本--3.1.0 实现代码 后语 easypoi版本--3.1.0 实现代码 public void export(HttpServletResponse response, H ...

  10. 用 Flutter 搭建标签+导航框架

    前言 在 Flutter 这个分类的第一篇文章总结了下最新的 Mac 搭建 Flutter 开发环境和对声明式UI这个理解的东西,前面也有提过,准备像在 SwiftUI 分类中那样花一些功夫来写一个 ...