壮志凌云,脚踏实地。
改变,坚持。

  昨天在看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()方法时,如果能显示调用它最好显式调用,以减少不必要的装箱。
posted on 2011-04-10 11:05  woxf  阅读(1148)  评论(3编辑  收藏  举报