小心!Struct陷阱

最近编码时遇到了一个问题,先看一个程序:

static void Main ( string[ ] args )
{
MyValueType mv
= new MyValueType ( b: 2.3 );
object objMv = mv;
Console.WriteLine ( mv );
Console.WriteLine ( objMv );
/* Step1 */
( (MyValueType) objMv ).Method1 (
3.4 );
Console.WriteLine ( objMv );
/* Step2 */
( (IMyValueType) mv ).Method1 (
4.5 );
Console.WriteLine ( mv );
/* Step3 */
( (IMyValueType) objMv ).Method1 (
5.6 );
Console.WriteLine ( objMv );

Console.ReadKey (
true);
}
struct MyValueType:IMyValueType{
public int a;
public double b;
public bool c;
public MyValueType ( int a=default(int),
double b=default(double),bool c=default(bool)) {
this.a=a;
this.b = b;
this.c = c;
}
public override string ToString ( )
{
return string.Format ( "a={0},b={1},c={2}", a, b,c );
//return base.ToString ( );
}
public void Method1 ( double b = default(double) )
{
this.b = b;
}
}
interface IMyValueType {
void Method1 ( double b = default(double) );
}

问:上述3个步骤的控制台输出是什么?(主要看b的值)

各位先猜猜b的值在3个步骤中输出是什么,然后复制粘贴运行下,看看是不是答对了!答对了的童鞋也请想想为什么会是这样的结果。

 

下面引出主题

Struct(值类型)的装箱和拆箱

一、预备知识

1.1 继承

所有值类型都是sealed的,即不能被继承。struct不能继承其它类型(其实已经继承自ValueType类了),不过可以实现多个接口。

1.2 初始化

struct中的每个Field在其默认构造函数中都初始化为default(xxx)类型的值,C#完全禁止了用户显示定义默认构造器,也不能在声明时对Field进行赋值。如果要自己编写构造函数,则必须对每个Field赋值,不能只对其中一些Field赋值。

1.3 存储

structclass不同,数据存储在栈上,而不是堆上。

二、与引用类型的转换

2.1 装箱步骤(值类型转换为引用类型)

1) 在堆中分配对应的内存空间

2) 内存复制操作,栈上的数据复制到堆中

3) 更新对象或接口引用,使之指向堆中的位置

装箱操作往往是隐式转换中用到的。

2.2拆箱操作(引用类型转换为值类型)

拆箱会解除对堆中对象的引用。一般地,拆箱完毕后会有复制内存的操作。拆箱操作必须显式转换。另外,必须拆箱为其基础类型,见下面代码:

int ia;
object ob;
double dc;
ia
=23;
ob
=ia;(装箱操作)
/*InvalidCastException,拆箱操作只能返回int类型,应确保源类型能转换成目标类型*/
//dc=(double)ob;
dc=(double)(int)ob;//正确,要首先拆箱为基础类型

三、开头代码的解释

第一步以前:

object objMv = mv;

该步骤引发装箱操作,会在堆上产生一个mv的内存副本,然后objMv指向该副本

第一步:

objMv要调用structMethod1函数,就必须拆箱为struct。这样,就会产生一个栈上的临时副本,函数Method1对临时副本进行了更改,但是不会被保存在堆上的objMv中。所以堆上的objMvb值不会改变

第二步:

mv要调用接口IMyValueType的函数Method1,就会被装箱为IMyValueType。这样,就会产生一个堆上的临时副本,函数Method1对临时副本进行了更改,但是不会影响栈上mv的内存数据。

第三步:

引用类型之间的转换,没有装拆箱操作,不会有复制操作,即Method1可以更改objMv中的值

四、总结

装箱、拆箱有一些容易被人忽视的特性。就像开头代码一样,以为修改了structobject的值,其实根本没有修改,从而引发致命错误。记住“不要创建可变值类型”,就会在很大程度上避免其中的多数问题。

Ps:有什么不足之处多多指教哈,谢谢~J

原文地址:https://www.cnblogs.com/ganmuren/p/2137958.html