参考:
Applied Microsoft .NET Framework Programming by Jeffrey Richter
CLR Via C#, Second Edition, by Jeffrey Richter
先回忆一下装箱和拆箱,看看下面这段代码有多少次装箱操作:
public static void Main()
{
Int32 v = 5;
Object o = v;//第一次,对v装箱,并将指针存放到o
v = 123;
//第二次,对v装箱,并将指针存放到堆栈等待Concat(拼接)操作
//第三次,对o拆箱,并将拆箱后的值再次装箱等待Concat(拼接)
Console.WriteLine(v + ", " + (Int32)o);
}
以上共进行了3次装箱。
下面的代码演示了值类型装箱易见的错误。
using System;
internal struct Point
{
private Int32 _x, _y;
public Point(Int32 x, Int32 y)
{
this._x = x;
this._y = y;
}
public void Change(Int32 x, Int32 y)
{
this._x = x;
this._y = y;
}
public override String ToString()
{
return String.Format("({0}, {1})", _x, _y);
}
}
public sealed class Program
{
public static void Main()
{
Point p = new Point(1, 1);
Console.WriteLine(p);
p.Change(2, 2);
Console.WriteLine(p);
Object o = p;
Console.WriteLine(o);
((Point)o).Change(3, 3);
Console.WriteLine(o);
}
}
结果显示:
(1, 1)
(2, 2)
(2, 2)
(2, 2)
最后一行没达到预期的(3, 3)是因为o装箱后,产生的新Point实例调用了Change(3, 3),而并没有真正调用o的Change方法。
但可以使用接口来骗过c#从而达到对已装箱值类型进行更改的目的
using System;
internal interface IChangeBoxedPoint
{
void Change(Int32 x, Int32 y);
}
internal struct Point : IChangeBoxedPoint
{
private Int32 _x, _y;
public Point(Int32 x, Int32 y)
{
this._x = x;
this._y = y;
}
public void Change(Int32 x, Int32 y)
{
this._x = x;
this._y = y;
}
public override String ToString()
{
return String.Format("({0}, {1})", _x, _y);
}
}
public sealed class Program
{
public static void Main()
{
Point p = new Point(1, 1);
Console.WriteLine(p);
p.Change(2, 2);
Console.WriteLine(p);
Object o = p;
Console.WriteLine(o);
((Point)o).Change(3, 3);
Console.WriteLine(o);
((IChangeBoxedPoint)p).Change(4, 4);
Console.WriteLine(p);
((IChangeBoxedPoint)o).Change(5, 5);
Console.WriteLine(o);
Console.Read();
}
}
结果为:
(1, 1)
(2, 2)
(2, 2)
(2, 2)
(2, 2)
(5, 5)
这里最后的结果是我们预期的,是因为o要转型为IChangeBoxedPoint,此时的o已经装箱。
所以值类型在进行装箱时要注意这点细节。
上面只是演示,所以采用了结构。其实Point用class写就可以避免上面的问题。
转载于:https://www.cnblogs.com/yurichou/archive/2007/07/12/816110.html