《Effective C#》之减少装箱和拆箱
《Effective C#》之减少装箱和拆箱_天极网 http://dev.yesky.com/msdn/359/3486359.shtml
《Effective C#》之减少装箱和拆箱
例如,对于如下简单的装箱和拆箱操作语句。
object obj = i;//Boxing
if( obj is int )
int j = (int) obj;//Unboxing
明白了这两名词的意思,现在说说为什么要减少装箱和拆箱操作。
原因有两个,主要是关于效率:一个就是对于堆的操作效率比较低;另一个就是对于堆上分配的内存资源,需要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#》之减少装箱和拆箱的更多相关文章
- C# 程序性能提升篇-1、装箱和拆箱,枚举的ToString浅析
前景提要: 编写程序时,也许你不经意间,就不知不觉的使程序代码,发生了装箱和拆箱,从而降低了效率,不要说就发生那么一次两次,如果说是程序中发生了循环.网络程序(不断请求处理的)等这些时候,减少装箱和拆 ...
- Java暗箱操作之自动装箱与拆箱
我以前在写Android项目的时候,估计写得最多最熟练的几句话就是: List<Integer> list = new ArrayList<Integer>(); list.a ...
- C#基础知识系列二(值类型和引用类型、可空类型、堆和栈、装箱和拆箱)
前言 之前对几个没什么理解,只是简单的用过可空类型,也是知道怎么用,至于为什么,还真不太清楚,通过整理本文章学到了很多知识,也许对于以后的各种代码优化都有好处. 本文的重点就是:值类型直接存储其值,引 ...
- C# 知识回顾 - 装箱与拆箱
装箱与拆箱 目录 生活中的装箱与拆箱 C# 的装箱与拆箱 值类型和引用类型 装箱 拆箱 生活中的装箱与拆箱 我们习惯了在网上购物,这次你想买本编程书 -- <C 语言从入门到放弃> ...
- [C#] C# 知识回顾 - 装箱与拆箱
装箱与拆箱 目录 生活中的装箱与拆箱 C# 的装箱与拆箱 值类型和引用类型 装箱 拆箱 读者见解 生活中的装箱与拆箱 我们习惯了在网上购物,这次你想买本编程书 -- <C 语言从入门到放弃 ...
- 深入理解C#的装箱和拆箱(转)
装箱和拆箱是值类型和引用类型之间相互转换是要执行的操作. 1. 装箱在值类型向引用类型转换时发生 2. 拆箱在引用类型向值类型转换时发生 光上述两句话不难理解,但是往深处了解,就需要一些篇幅来解释了 ...
- 转载:详解Java 自动装箱与拆箱的实现原理
原文:http://www.jb51.net/article/111847.htm 什么是自动装箱和拆箱 自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对 ...
- CLR via 笔记 5.3 值类型的装箱和拆箱
1.装箱 为了将一个值类型转换成一个引用类型,要使用一个名为装箱(Boxing)的机制. 1.在托管堆中分配好内存.分配的内存量是值类型的各个字段需要的内存量加上托管堆的所有对象都有的两个额外成员(类 ...
- [C#] 类型学习笔记一:CLR中的类型,装箱和拆箱
在学习.NET的时候,因为一些疑问,让我打算把.NET的类型篇做一个总结.总结以三篇博文的形式呈现. 这篇博文,作为三篇博文的第一篇,主要探讨了.NET Framework中的基本类型,以及这些类型一 ...
随机推荐
- RxJava +Retrofit 简单使用
1.添加依赖 compile 'com.squareup.retrofit2:converter-gson:2.3.0' compile 'com.squareup.retrofit2:adapter ...
- Python-对比参考目录查找多个文件夹中不同的文件
改完文件名称后,Dr.he 发现分别保存5个状态的jpg 文件的文件夹出现缺少文件的情况.为了让他少熬夜查找缺失文件,结合网友分享的脚本,写了查找以下代码,满足他的需求,也以防自己忘记.以下代码能解决 ...
- Azure Terraform(二)语法详解
一,引言 上篇文章开始,我们简单介绍了以下通过基础设施管理工具----- Terraform,通过它来统一管理复杂的云基础设施资源.作为入门演示,使用Terraform 部署Azure 资源组的方式直 ...
- Redis 设计与实现 6:五大数据类型之字符串
前文 Redis 设计与实现 2:Redis 对象 说到,五大数据类型都会封装成 RedisObject. typedef struct redisObject { unsigned type:4; ...
- shiro 拦截时序图
shiro 集成 web 1.第一个过滤器-AbstractShiroFilter subject 是后续动作的主体. 首先构造 subject: WebSubject DefaultSecurity ...
- checkBox判断是否选中的方法
这里可以分为两种情况:JQuery对象和DOM对象: 通常我们用JQuery判断元素的属性的时候喜欢用 attr("attrName"); 但是尝试过的同学可能都知道,这种方法判断 ...
- Linux USB子系统(一)—— USB设备基础概念
一.基础概念 在终端用户看来,USB设备为主机提供了多种多样的附加功能,如文件传输,声音播放等,但对USB主机来说,它与所有USB设备的接口都是一致的.一个USB设备由3个功能模块组成:USB总线接口 ...
- [LeetCode]172. Factorial Trailing Zeroes阶乘尾随0的个数
所有的0都是有2和45相乘得'到的,而在1-n中,2的个数是比5多的,所以找5的个数就行 但是不要忘了25中包含两个5,125中包含3个5,以此类推 所以在找完1-n中先找5,再找25,再找125.. ...
- ServletContext的作用
一个项目只有一个ServletContext对象,一个tomcat有多个项目 作用:在多核Servlet中来获取这个唯一的对象,使用ta给多个Servlet传递数据. 在Tomcat启动时创建,在To ...
- java零基础之--JDK安装篇
---恢复内容开始--- 很多零基础学习者在开始学习java中很难理解JDK的安装和配置,以下是基于Windows 7 的安装配置流程(Windows 10类似) 1. 在安装之前我们先了解几个名词: ...