{
private int _Hours;
private int _Minutes;
private int _Seconds;
public int Hours
{
get { return _Hours; }
}
public int Minutes
{
get { return _Minutes; }
}
public int Seconds
{
get { return _Seconds; }
}
public Angle(int hours,int minutes,int seconds)
{
_Hours = hours;
_Minutes = minutes;
_Seconds = seconds;
}
public Angle(int hours,int minutes):this(hours,minutes,default(int))
{
}
public Angle Move(int hours,int minutes,int seconds)
{
return new Angle(Hours + hours, minutes + Minutes, seconds + Seconds);
}
public void MoveTo(int hours,int minutes,int seconds)
{
_Hours = hours;
_Minutes = minutes;
_Seconds = seconds;
}
}
interface IAngle
{
void MoveTo(int hours, int minutes, int seconds);
}
class Program
{static void Main(string[] args)
{
Angle angle = new Angle(25, 58, 23);
object objectAngle = angle;//装箱,值类型付给引用类型
Console.Write(((Angle)objectAngle).Hours);//print 25 拆箱
((Angle)objectAngle).MoveTo(26, 58, 23);//修改值,为了调用MoveTo需将objectAngle拆箱并创建一个值的副本。虽然栈值改变、但会被丢弃 ,在objectAngle引用堆不发生改变
Console.Write(", " + ((Angle)objectAngle).Hours);//print 25
((IAngle)angle).MoveTo(26, 58, 23);//值被转型为接口IAngle变量,即发生装箱,运行时将angle数据复制到堆上,在调用返回前会在堆上操作,结果不会发生从堆到栈的复制。相反修改过得堆数据准备好进行垃圾回收,而angle中的数据保持未更改状态
Console.Write(", " + ((Angle)angle).Hours);// print 25
((IAngle)objectAngle).MoveTo(26, 58, 23);//向IAngle转型不发生复制动作,即两个引用型不发生装箱操作 ,更新Hours的值会改变
Console.Write(", " + ((Angle)objectAngle).Hours);//print 26
Console.Read();
}
拆箱指令不包括将数据复制回栈的动作,在C#中只有将值类型作为引用类型的一个字段访问才能直接访问堆上的值类型。接口是引用类型,所以通过接口访问已装箱的值可避免拆箱和复制
当调用值类型的接口方法,实例必须是一个变量,方法可能改变这个值。拆箱会生成一个托管地址,运行时有一个存储位置和变量。结果运行时只传递托管地址而不进行拆箱操作。
避免拆箱:
int number;
object thing;
number = 42;
thing = number;
string text = ((IFormattable)thing).ToString("X",null);
Console.WriteLine(text);
接口属于引用类型,所以调用某成员时不需要拆箱。除此之外,调用struct的tostring()方法(重写了object的tostring()方法)也不需要拆箱。编译时从struct的tostring()调用值类型都是密封的。