2.toString() 会发生装箱吗?

最近被问道了这么问题,我当时回答是会的,因为.toString()会把值类型2转换成引用类型,所以会发生装箱。

后来总觉有些不妥当,所以查阅一些资料并参考网络上的讨论:

拆箱装箱的官方解释

Boxing is the process of converting a value type to the type object or to any interface type implemented by this value type. When the CLR boxes a value type, it wraps the value inside a System.Object and stores it on the managed heap. Unboxing extracts the value type from the object. Boxing is implicit; unboxing is explicit. The concept of boxing and unboxing underlies the C# unified view of the type system in which a value of any type can be treated as an object.

装箱用于在托管内存中存储值类型。 装箱是是值类型到 object 类型或到此值类型所实现的任何接口类型的隐式转换。 对值类型装箱会在堆中分配一个对象实例,并将该值复制到新的对象中。

下面是官方的几个拆箱/装箱的例子:

eg1:
int i = 123;
object o = i;//隐式装箱

eg2:
String.Concat("Answer", 42, true) //42和true都会发生装箱

eg3:
List<object> mixedList = new List<object>();
mixedList.Add("First Group:");
for (int j = 1; j < 5; j++)
{
    mixedList.Add(j);//在添加时,j先装箱
}
var sum = 0;for (var j = 1; j < 5; j++)
{
    //下面的一行会发生编译错误: 
    //Operator ‘*‘ cannot be applied to operands of type ‘object‘ and ‘object‘. 
  //sum += mixedList[j] * mixedList[j]);
  
  //下面经过拆箱就不会出现上面的编译错误.
  sum += (int)mixedList[j] * (int)mixedList[j];
}

Note: 

相对于简单的赋值而言,装箱和取消装箱过程需要进行大量的计算。 对值类型进行装箱时,必须分配并构造一个新对象。 取消装箱所需的强制转换也需要进行大量的计算,只是程度较轻。

更多性能的了解:https://msdn.microsoft.com/zh-cn/library/ms173196.aspx

再引入图片来在说明内存中的变化:

int i = 123;
object o = i;//隐式装箱

int i = 123;      // a value type
object o = i;     // boxing
int j = (int)o;   // unboxing

看值类型有没有进行拆箱,就看他有没有装换成Object或者值类型所继承的接口类型...

Int.ToString 此方法中,值类型转换成ValueType类型,不满足装箱的条件(可以查看下面IL代码),可以判定Int.ToString是没有装箱的。

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // 代码大小       45 (0x2d)
  .maxstack  3
  .locals init ([0] int32 v, [1] object o)
  IL_0000:  nop
  IL_0001:  ldc.i4.5
  IL_0002:  stloc.0
  IL_0003:  ldloc.0
  IL_0004:  box        [mscorlib]System.Int32
  IL_0009:  stloc.1
  IL_000a:  ldloca.s   v
  IL_000c:  call       instance string [mscorlib]System.Int32::ToString()
  IL_0011:  ldstr      ","
  IL_0016:  ldloc.1
  IL_0017:  unbox.any  [mscorlib]System.Int32
  IL_001c:  box        [mscorlib]System.Int32
  IL_0021:  call       string [mscorlib]System.String::Concat(object,
                                                              object,
                                                              object)
  IL_0026:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_002b:  nop
  IL_002c:  ret
} // end of method Program::Main

了解了上面的信息之后以后就知道下面建议用哪一个了吧:

int num = 3;
//用下面的哪个呢?请思考
string numStr = string.Format("{0}", num);
string numStr = string.Format("{0}", num.ToString());

参考:

https://msdn.microsoft.com/en-us/library/yz2be5wk.aspx

http://www.cnblogs.com/DebugLZQ/archive/2012/09/02/2667835.html

http://q.cnblogs.com/q/44027

http://bbs.csdn.net/topics/360191652

时间: 2024-08-10 02:33:11

2.toString() 会发生装箱吗?的相关文章

值类型的装箱和拆箱

在CLR中为了将一个值类型转换成一个引用类型,要使用一个名为装箱的机制. 下面总结了对值类型的一个实例进行装箱操作时内部发生的事: 1)在托管堆中分配好内存.分配的内存量是值类型的各个字段需要的内存量加上托管堆上的所有对象都有的两个额外成员(类型对象指针和同步块索引)需要的内存量. 2)值类型的字段复制到新的分配的堆内存. 3)返回对象的地址.现在,这个地址是对一个对象的引用,值类型现在是一个引用类型. 拆箱不是直接将装箱过程倒过来.拆箱的代价比装箱低得多.拆箱其实就是一个获取一个指针的过程,该

c#的装箱和拆箱及值类型和引用类型

装箱:它允许根据值类型创建一个对象,然后使用对这新对象的一个引用. int i = 5; object o = i; int j = (int)o; 装箱:运行时将在堆上创建一个包含值5的对象(它是一个普通对象).o的值是对该对象的一个引用.该对象的值是原始值的一个副本,改变 i 的值不会改变箱内的值. 拆箱:第三行,必须告诉编译器将object拆箱成什么类型,同样,拆箱也会复制箱内的值,在赋值之后,j和该对象之间不再有任何关系. 拆箱和装箱的发生是时间:拆箱一般是很容易看出来,因为要在代码中明

几种情况发生装箱

装箱是将值类型转换为引用类型,拆箱便是将应用类型转为值类型. MyClass myClass = new MyClass(); 1.Object ob = myClass; //会发生装箱 2.(Object)myClass;//强制转换会发生装箱 3.void GetClass(Object ob)----调用方法GetClass(myClass)//发生装箱 拆箱:Object ob = new MyClass(); MyClass myClass = (MyClass)ob;

装箱和拆箱 深度理解

提问: 首先我们来提两个疑问,我们自定义了一个类如Customclass类型, Customclass myclass=new Customclass() Object obj=myclass; 运行上面这段代码,我们会进行装箱操作吗? 基础知识: .Net的类型分为两种,一种是值类型(Value Type ),另一种是引用类型(Reference Type).这两个类型的本质区别,值类型数据是分配在栈中,而引用类型数据分配在堆上.那么如果要把一个值类型数据放到堆上,就需要装箱操作:反之,把一个

12.时间日期,字符串,内部类,拆装箱

一.String,StringBuffer,StringBuilder1.String是一个不可变的常量,每次修改都会产生新的对象2.StringBuffer,StringBuilder都是可变字符序列,是字符串的增强版.3.StringBuffer,StringBuilder作用:主要是当业务大量修改字符串的时使用4.区别:StringBuffer是线程安全,StringBuild非线程安全 二.自动拆装箱1.基本类型转换为封装类型为装箱过程,反之则是拆箱过程2.八种基本数据类型对应的封装类型

java 装箱和拆箱

每次看到某篇博客里面说要静下心来欣赏源码,我都很感慨,我的耐心没人家好.是真的要静下心来读源码,因为说不定就像作者说的那样 “说不定你就发现了一个优秀的设计呢!!!” 才不久就被同事说了,学知识的时候不要看野史,要看官方.我还是做不到.官方是一定要看的,但是野史还是照样翻,因为我觉得很多野史写的很生动,甚至有滋有味,有声有色,能从作者的言词中感受到人家的认真,不知不觉中能成为自己的榜样. 都是同行,为什么人家就能那么优秀,我要向其学习. 题外话 今天早上在学习公司代码,然后准备学习下MVP框架,

CLR via C#深解笔记三 - 基元类型、引用类型和值类型 | 类型和成员基础 | 常量和字段

编程语言的基元类型 某些数据类型如此常用,以至于许多编译器允许代码以简化的语法来操纵它们. System.Int32 a = new System.Int32();  // a = 0 a = 1; 等价于: int a = 1; 这种语法不仅增强了代码的可读性,其生成的IL代码与使用System.Int32时生成的IL代码是完全一致的. 编译器直接支持的数据类型称为基元类型(primitive type).基元类型直接映射到Framework类库(FCL)中存在的类型.如C#中,int直接映射

C#中String StringBuilder StringBuffer

String和StringBuilder和StringBuffer,这三个都是值得深究一翻的,可能很多人会说,实在不行的话,都全部用StringBuilder,啥事没有,我不能说你的想法事不正确的,但是我可以给出更好的建议.下面简单介绍一下这三个类.  String类 在我们平时的使用当中很容易不注意到的是,自己写的代码很容易发生了装箱的操作(把值类型转换为引用类型).就比如很常见的,一个字符串拼接 string str=9+"test"; 通过查看IL代码可以知道这里发生了装箱的操作

改善C#编程的50个建议(41-45)

-------------------------翻译 By Cryking----------------------------- -----------------------转载请注明出处,谢谢!------------------------ 41 为数据驱动的动态类型使用DynamicObject或IDynamicMetaObjectProvider 动态编程的一个优势是当公共接口在运行时改变了,它会基于你怎么使用而建立新的类型.C#提供了System.Dynamic.Dynamic