一个重写的ToString()方法引发的装箱

  昨天在看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
      综上可知,当我们在值类型中重写了.ToString()方法时,如果能显示调用它最好显式调用,以减少不必要的装箱。

你可能感兴趣的:(toString)