首页 新闻 会员 周边

值类型调用ToString方法会不会装箱

0
悬赏园豆:20 [待解决问题]
 1 Struct A : ICloneable 
 2   { 
 3       public Int32 x; 
 4       public override String ToString() { 
 5         return String.Format("{0}",x); 
 6       } 
 7       public object Clone()  { 
 8         return MemberwiseClone(); 
 9       } 
10   } 
11   static void main() 
12   { 
13     A a; 
14     a.x = 100; 
15     Console.WriteLine(a.ToString()); 
16     Console.WriteLine(a.GetType()); 
17     A a2 = (A)a.Clone(); 
18     ICloneable c = a2; 
19     Ojbect o = c.Clone(); 
20   } 
21   5.0:a.ToString()。编译器发现A重写了ToString方法,会直接调用ToString的指令。因为A是值类型,编译器不会出现多态行为。
因此,直接调用,不装箱。(注:ToString是A的基类System.ValueType的方法)

上文是重写了ToString的方法,不装箱。但是,如果不重写ToString的方法,当值类型以字符串形式出现的时候,会不会装箱呢~?

kk_liang的主页 kk_liang | 初学一级 | 园豆:182
提问于:2012-11-28 12:46
< >
分享
所有回答(5)
0
chenping2008 | 园豆:9836 (大侠五级) | 2012-11-28 13:09

调用ValueType的ToString,有些情况还是会进行装箱的

例如,值类型的ToString方法在内部调用base.ToString()

支持(0) 反对(0) kk_liang | 园豆:182 (初学一级) | 2012-11-28 14:44

@kk_liang: 

呵呵 只要 ValueType 的 ToString没有调用 base.ToString() 就行了啊

支持(0) 反对(0) Qlin | 园豆:2403 (老鸟四级) | 2012-11-28 16:45
1

现在大部分所说的装箱都有一个误解就是转换成引用类型,

其实  装箱 是 值类型要转换成   Object 或者是该值类型继承的  接口类型。参考MSDN

一般 就是看 有没有和object发生类型转换,理解这点,就好办了。

你例子中 的 a.ToString是 a 本身的 ToString,不发生类型转换,当然不装箱。

如果A 不重写ToString,a.ToString() 发生了类型转换,就是转换成了ValueType,再调用的ToString。并没有转换成object,当然不发生装箱。

Qlin | 园豆:2403 (老鸟四级) | 2012-11-28 13:24

我不同意你的看法...

1,《CLR Via C#》里面说到,为了将一个值类型转换成一个引用类型,要使用一个名为装箱的机制。

这里说到的,可以这么理解--值类型装换成引用类型,就是一个装箱的过程,对吧~

2,  装箱 是 值类型要转换成   Object 或者是该值类型继承的  接口类型。

按照上面所说的,根据用户自定义的类型,再进行相互装换的话,还是会进行装箱的

例子源用MSDN的http://msdn.microsoft.com/zh-cn/library/xhbhezf4.aspx

然后我再看了例子的IL代码,结果如下

 1 .method private hidebysig static void  Main() cil managed
 2 {
 3   .entrypoint
 4   // 代码大小       99 (0x63)
 5   .maxstack  2
 6   .locals init ([0] class ConsoleApplication2.Fahrenheit fahr,
 7            [1] class ConsoleApplication2.Celsius c,
 8            [2] class ConsoleApplication2.Fahrenheit fahr2)
 9   IL_0000:  nop
10   IL_0001:  ldc.r4     100.
11   IL_0006:  newobj     instance void ConsoleApplication2.Fahrenheit::.ctor(float32)
12   IL_000b:  stloc.0
13   IL_000c:  ldstr      "{0} Fahrenheit"
14   IL_0011:  ldloc.0
15   IL_0012:  callvirt   instance float32 ConsoleApplication2.Fahrenheit::get_Degrees()
16   IL_0017:  box        [mscorlib]System.Single
17   IL_001c:  call       void [mscorlib]System.Console::Write(string,
18                                                             object)
19   IL_0021:  nop
20   IL_0022:  ldloc.0
21   IL_0023:  call       class ConsoleApplication2.Celsius ConsoleApplication2.Fahrenheit::op_Explicit(class ConsoleApplication2.Fahrenheit)
22   IL_0028:  stloc.1
23   IL_0029:  ldstr      " = {0} Celsius"
24   IL_002e:  ldloc.1
25   IL_002f:  callvirt   instance float32 ConsoleApplication2.Celsius::get_Degrees()
26   IL_0034:  box        [mscorlib]System.Single
27   IL_0039:  call       void [mscorlib]System.Console::Write(string,
28                                                             object)
29   IL_003e:  nop
30   IL_003f:  ldloc.1
31   IL_0040:  call       class ConsoleApplication2.Fahrenheit ConsoleApplication2.Celsius::op_Explicit(class ConsoleApplication2.Celsius)
32   IL_0045:  stloc.2
33   IL_0046:  ldstr      " = {0} Fahrenheit"
34   IL_004b:  ldloc.2
35   IL_004c:  callvirt   instance float32 ConsoleApplication2.Fahrenheit::get_Degrees()
36   IL_0051:  box        [mscorlib]System.Single
37   IL_0056:  call       void [mscorlib]System.Console::WriteLine(string,
38                                                                 object)
39   IL_005b:  nop
40   IL_005c:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
41   IL_0061:  pop
42   IL_0062:  ret
43 } // end of method MainClass::Main

第16行和26行说明还是会进行装箱操作的,而且, Celsius和Fahrenheit这两个类不是相互类型所继承的接口类型...

以上均是给人观点,请猛拍

支持(0) 反对(0) kk_liang | 园豆:182 (初学一级) | 2012-11-28 15:15

@Qlin 大哥,第2点我说错了,那是WriteLine方法参数转为Object的时候装箱的,只是调用各自类中的方法而已...

支持(0) 反对(0) kk_liang | 园豆:182 (初学一级) | 2012-11-28 15:26

@kk_liang: 

大哥   Il代码看着头痛,不太懂 呵呵,你的 16 行 和 26行是不是 创建对象时,值类型保存到 托管堆中。

 Celsius和Fahrenheit 两个都是引用类型,都是在托管堆中,互相转换,谈不上 装箱,甚至不会申请新的内存。

支持(0) 反对(0) Qlin | 园豆:2403 (老鸟四级) | 2012-11-28 16:28

@Qlin: 16行和26行是调用 console.WriteLine(string,object)的时候,参数如你所说的转换Object类型是发生装箱的,所以是我搞错了,不好意思啊~

基本现在理清一下思路就是,Int.ToString()是不需要装箱的,会调用ValueType的ToString方法...

支持(0) 反对(0) kk_liang | 园豆:182 (初学一级) | 2012-11-29 21:02

@kk_liang: Int.ToString不需要装箱是因为它本身正确地实现了ToString方法,而且并不是调用的ValueType.ToString,调用ValueType.ToString会导致装箱。

支持(0) 反对(0) 水牛刀刀 | 园豆:6350 (大侠五级) | 2012-11-30 10:06

@水牛刀刀: 您这么说我又搞不懂了~本身实现了ToString方法,那也就是从值类型转换成引用类型对吧~为什么不装箱呢~?

支持(0) 反对(0) kk_liang | 园豆:182 (初学一级) | 2012-12-04 16:54
0

不会,CLR Via C# 建议用 ToString() 的

geass.. | 园豆:1821 (小虾三级) | 2012-11-28 18:11

看我的答案。

支持(0) 反对(0) 水牛刀刀 | 园豆:6350 (大侠五级) | 2012-11-30 10:03
0

如果你的值类型自己override了ToString方法,那么不会装箱。否则,会调用ValueType.ToString方法,由于ValueType是类,所以要装箱。你可以去看生成的IL里是否有box指令。

水牛刀刀 | 园豆:6350 (大侠五级) | 2012-11-28 23:02

我试过了,不会装箱...

支持(0) 反对(0) kk_liang | 园豆:182 (初学一级) | 2012-11-29 21:03

@kk_liang: 我刚看了下,没有box指令,但是有constrained指令,这个指令的作用详细看这里, 重点部分我贴出来:

  • If thisType is a reference type (as opposed to a value type) then ptr is dereferenced and passed as the 'this' pointer to the callvirt of method.

  • If thisType is a value type and thisType implements method then ptr is passed unmodified as the 'this' pointer to a call  method instruction, for the implementation of methodby thisType.

  • If thisType is a value type and thisType does not implement method then ptr is dereferenced, boxed, and passed as the 'this' pointer to the callvirt  method instruction.

这里是最后一种情况,因此会装箱。以上可能太理论,不过我建议以后碰到类似问题你可以自己实践一下:比如创建2个struct A和B,一个实现ToString另外一个不实现,然后调用他们的ToString方法各100000次,比较一下时间,就很容易得出结论(我已经试过了,证实了我的说法是对的)。注意测试的时候应当是RELEASE编译,并且注意JIT的影响。

支持(0) 反对(0) 水牛刀刀 | 园豆:6350 (大侠五级) | 2012-11-30 10:03

@水牛刀刀: 已学习,谢谢指教。待会去测试下看看。三克油啊。

支持(0) 反对(0) ````` | 园豆:14268 (专家六级) | 2012-11-30 10:11

@水牛刀刀: 我试过了下,没有出现constrained指令?为啥?

 

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // 代码大小       12 (0xc)
  .maxstack  1
  .locals init ([0] int32 i,
           [1] string s)
  IL_0000:  nop
  IL_0001:  ldc.i4.5
  IL_0002:  stloc.0
  IL_0003:  ldloca.s   i
  IL_0005:  call       instance string [mscorlib]System.Int32::ToString()
  IL_000a:  stloc.1
  IL_000b:  ret
} // end of method Program::Main

支持(0) 反对(0) kk_liang | 园豆:182 (初学一级) | 2012-12-03 16:50

@kk_liang: 你用Int32做实验?Int32自己实现了ToString方法,当然不会装箱。你问题里不是一个自定义的struct么。

支持(0) 反对(0) 水牛刀刀 | 园豆:6350 (大侠五级) | 2012-12-03 17:25

@水牛刀刀: 嗯~是的,这次真的搞懂了什么回事了~...

昨天的博文不是说这个问题的,就是说Int32.ToString()的,我一个心急把它给删了~呵呵~...

谢谢你的耐心回答...

支持(0) 反对(0) kk_liang | 园豆:182 (初学一级) | 2012-12-04 11:10
0

不会的!

jason2013 | 园豆:1998 (小虾三级) | 2012-11-29 09:17

看我的答案。

支持(0) 反对(0) 水牛刀刀 | 园豆:6350 (大侠五级) | 2012-11-30 10:03
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册