经常被问到这样的问题:值类型能不能按引用传递?传递之后又是什么样的处理方式
当然是可以的,不管是现在还是以前都可以。我们来看看下面两个方法的区别
static void TestMethod(int a) {a = 5;Console.WriteLine(a);}static void TestMedhot2(ref int a) {a = 5;Console.WriteLine(a);}
我们知道int是值类型,在第一个方法中,我们按照默认的方式进行传递,其实就是所谓的按值传递。
我们看到在IL_0002这个地方,是把a这个变量压入了栈。也就是说,此时在栈中是有一个a变量代表的值的 ,例如5
然后我们再来看另外一个方法。首先从直观上看,int32&和int32肯定是有区别的,我们都知道在C++中,&表示指针的意思
然后,我们并没有看到它将a这个变量压入栈中。那么他到底是做了什么事情呢
ldind.* : 间接载入指令,通过指针获取数据。 stind.* : 间接存储指令,通过指针存储数据。注意,虽然按照引用传递,但这个与“装箱和拆箱”还不是一回事情。什么时候发生装箱拆箱呢?就是将值类型转换为了引用类型(一般指Object).假设我们如下的方法
static void TestMethod3(object a) {Console.WriteLine(a);}
然后,我们在Main里面去调用
int a = 7;TestMethod(a);TestMedhot2(ref a);TestMethod3(a);
这里调用TestMethod3的时候会发生装箱
也就是说,如果仅仅是按引用传递参数的话,那么不会发生装箱和拆箱的问题。也就是说,它并没有产生另外一份数据,而是用指针的方式指向了参数所代表的那份数据而已(这份数据可能在栈上面,也可能在堆上面),但总之是一个指针引用,所以说,按照引用传递的情况,我们如果在TestMetho2中修改了a的值,那么后续访问a这个变量,它的值就确实被改变了。
思考一下:为什么说可能在堆上面呢?
下面有一个完整的例子
using System;namespace ConsoleApplication1
{class Program{static void Main(string[] args){int a = 7;Console.WriteLine("原始值:{0}",a);TestMethod(a);Console.WriteLine("按值传递调用后:{0}", a);TestMedhot2(ref a);Console.WriteLine("按引用传递调用后:{0}", a);TestMethod3(a);//这里发生装箱Console.WriteLine("装箱操作传递调用后:{0}", a);Console.WriteLine(a);Console.Read();}static void TestMethod(int a) {a = 5;Console.WriteLine(a);}static void TestMedhot2(ref int a) {a = 5;Console.WriteLine(a);}static void TestMethod3(object a) {int _a = (int)a;//这里发生拆箱Console.WriteLine(_a);}}
}