昨天在看CLR的装箱拆箱时,对于值类型:同样的定义,一个在输出控制台时显示调用了重写的ToString()与另一个未显示调用ToString()方法时,前者不用装箱而后者需装箱时产生了疑惑。今天动手实验了下,在对其后台的IL代码分析得知果然如此。
定义代码:
1 namespace ConsoleApplication3 2 { 3 internal interface IChangeBoxedPoint 4 { 5 void Change(Int32 x, Int32 y); 6 } 7 internal struct Point : IChangeBoxedPoint 8 { 9 private Int32 m_x, m_y; 10 11 public Point(Int32 x, Int32 y) 12 { 13 m_x = x; 14 m_y = y; 15 } 16 public void Change(Int32 x, Int32 y) 17 { 18 m_x = x; 19 m_y = y; 20 } 21 22 23 public override string ToString() 24 { 25 return String.Format( " ({0}, {1}) " , m_x, m_y); 26 // return base.ToString(); 27 } 28 } 29 }
不显示调用上述 ToString()时代码:
class Program { static void Main( string [] args) { Point p = new Point( 1 , 1 ); Point p2 = new Point( 20 , 20 ); // Console.WriteLine(p2.ToString()); Console.WriteLine(p2); // 装箱,输出(20,20) // Console.WriteLine(p.ToString()); Console.WriteLine(p); // 装箱,输出(1,1) p.Change( 2 , 2 ); // Console.WriteLine(p.ToString()); Console.WriteLine(p); // 装箱,输出(2,2) Object o = p; // 装箱 Console.WriteLine(o); // 不论显式调用.ToString()与否,上面都需装箱 #region // ((Point)o).Change(3, 3); // Console.WriteLine(o.ToString()); // Console.WriteLine(o); // ((IChangeBoxedPoint)p).Change(4, 4); // Console.WriteLine(p); // ((IChangeBoxedPoint)o).Change(5, 5); // Console.WriteLine(o); #endregion Console.ReadKey(); } }
上述非显式调用时生成的IL代码:
.method private hidebysig static void Main( string [] args) cil managed{ .entrypoint // 代码大小 90 (0x5a) .maxstack 3 .locals init ([ 0 ] valuetype ConsoleApplication3.Point p, [ 1 ] valuetype ConsoleApplication3.Point p2, [ 2 ] object o) IL_0000: nop IL_0001: ldloca.s p IL_0003: ldc.i4. 1 IL_0004: ldc.i4. 1 IL_0005: call instance void ConsoleApplication3.Point::.ctor(int32, int32) IL_000a: nop IL_000b: ldloca.s p2 IL_000d: ldc.i4.s 20 IL_000f: ldc.i4.s 20 IL_0011: call instance void ConsoleApplication3.Point::.ctor(int32, int32) IL_0016: nop IL_0017: ldloc. 1 IL_0018: box ConsoleApplication3.Point //对P2的装箱 IL_001d: call void [mscorlib]System.Console::WriteLine( object ) IL_0022: nop IL_0023: ldloc. 0 IL_0024: box ConsoleApplication3.Point //对P的装箱 IL_0029: call void [mscorlib]System.Console::WriteLine( object ) IL_002e: nop IL_002f: ldloca.s p IL_0031: ldc.i4. 2 IL_0032: ldc.i4. 2 IL_0033: call instance void ConsoleApplication3.Point::Change(int32, int32) IL_0038: nop IL_0039: ldloc. 0 IL_003a: box ConsoleApplication3.Point //对P的装箱 IL_003f: call void [mscorlib]System.Console::WriteLine( object ) IL_0044: nop IL_0045: ldloc. 0 IL_0046: box ConsoleApplication3.Point //对P的装箱 IL_004b: stloc. 2 IL_004c: ldloc. 2 IL_004d: call void [mscorlib]System.Console::WriteLine( object ) IL_0052: nop IL_0053: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() IL_0058: pop IL_0059: ret} // end of method Program::Main
显示调用.ToString()方法时的代码:
View Code
1 namespace ConsoleApplication3 2 { 3 class Program 4 { 5 static void Main( string [] args) 6 { 7 Point p = new Point( 1 , 1 ); 8 Point p2 = new Point( 20 , 20 ); 9 10 Console.WriteLine(p2.ToString()); // 不装箱,输出(20,20) 11 // Console.WriteLine(p2); // 装箱,输出(20,20) 12 13 Console.WriteLine(p.ToString()); // 不装箱,输出(1,1) 14 // Console.WriteLine(p); // 装箱,输出(1,1) 15 16 p.Change( 2 , 2 ); 17 Console.WriteLine(p.ToString()); // 不装箱,输出(2,2) 18 // Console.WriteLine(p); // 装箱,输出(2,2) 19 20 Object o = p; // 装箱 21 // Console.WriteLine(o); // 不论显式调用.ToString()与否,上面都需装箱 22 Console.WriteLine(o.ToString()); // 不论显式调用.ToString()与否,上面都需装箱 23 #region 24 // ((Point)o).Change(3, 3); 25 // Console.WriteLine(o.ToString()); 26 // Console.WriteLine(o); 27 28 29 // ((IChangeBoxedPoint)p).Change(4, 4); 30 // Console.WriteLine(p); 31 32 // ((IChangeBoxedPoint)o).Change(5, 5); 33 // Console.WriteLine(o); 34 #endregion 35 36 Console.ReadKey(); 37 38 } 39 } 40 41 }
生成的IL代码:
1 .method private hidebysig static void Main( string [] args) cil managed 2 { 3 .entrypoint 4 // 代码大小 116 (0x74) 5 .maxstack 3 6 .locals init ([ 0 ] valuetype ConsoleApplication3.Point p, 7 [ 1 ] valuetype ConsoleApplication3.Point p2, 8 [ 2 ] object o) 9 IL_0000: nop 10 IL_0001: ldloca.s p 11 IL_0003: ldc.i4. 1 12 IL_0004: ldc.i4. 1 13 IL_0005: call instance void ConsoleApplication3.Point::.ctor(int32, 14 int32) 15 IL_000a: nop 16 IL_000b: ldloca.s p2 17 IL_000d: ldc.i4.s 20 18 IL_000f: ldc.i4.s 20 19 IL_0011: call instance void ConsoleApplication3.Point::.ctor(int32, 20 int32) 21 IL_0016: nop 22 IL_0017: ldloca.s p2 23 IL_0019: constrained. ConsoleApplication3.Point 24 IL_001f: callvirt instance string [mscorlib]System.Object::ToString() 25 IL_0024: call void [mscorlib]System.Console::WriteLine( string ) 26 IL_0029: nop 27 IL_002a: ldloca.s p 28 IL_002c: constrained. ConsoleApplication3.Point 29 IL_0032: callvirt instance string [mscorlib]System.Object::ToString() 30 IL_0037: call void [mscorlib]System.Console::WriteLine( string ) 31 IL_003c: nop 32 IL_003d: ldloca.s p 33 IL_003f: ldc.i4. 2 34 IL_0040: ldc.i4. 2 35 IL_0041: call instance void ConsoleApplication3.Point::Change(int32, 36 int32) 37 IL_0046: nop 38 IL_0047: ldloca.s p 39 IL_0049: constrained. ConsoleApplication3.Point 40 IL_004f: callvirt instance string [mscorlib]System.Object::ToString() 41 IL_0054: call void [mscorlib]System.Console::WriteLine( string ) 42 IL_0059: nop 43 IL_005a: ldloc. 0 44 IL_005b: box ConsoleApplication3.Point //唯一的一次装箱 45 IL_0060: stloc. 2 46 IL_0061: ldloc. 2 47 IL_0062: callvirt instance string [mscorlib]System.Object::ToString() 48 IL_0067: call void [mscorlib]System.Console::WriteLine( string ) 49 IL_006c: nop 50 IL_006d: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() 51 IL_0072: pop 52 IL_0073: ret 53 } // end of method Program::Main