再谈CLR:值类型按照引用传递(以及与装箱拆箱的区别)

经常被问到这样的问题:值类型能不能按引用传递?传递之后又是什么样的处理方式

 

当然是可以的,不管是现在还是以前都可以。我们来看看下面两个方法的区别

        static void TestMethod(int a) {
            a = 5;
            Console.WriteLine(a);
        }

        static void TestMedhot2(ref int a) {
            a = 5;
            Console.WriteLine(a);
        }

image

我们知道int是值类型,在第一个方法中,我们按照默认的方式进行传递,其实就是所谓的按值传递。

image

我们看到在IL_0002这个地方,是把a这个变量压入了栈。也就是说,此时在栈中是有一个a变量代表的值的 ,例如5

 

然后我们再来看另外一个方法。首先从直观上看,int32&和int32肯定是有区别的,我们都知道在C++中,&表示指针的意思

image

然后,我们并没有看到它将a这个变量压入栈中。那么他到底是做了什么事情呢

  • ldind.* : 间接载入指令,通过指针获取数据。
  • stind.* : 间接存储指令,通过指针存储数据。

    注意,虽然按照引用传递,但这个与“装箱和拆箱”还不是一回事情。什么时候发生装箱拆箱呢?就是将值类型转换为了引用类型(一般指Object).假设我们如下的方法

            static void TestMethod3(object a) {
                Console.WriteLine(a);
            }

    然后,我们在Main里面去调用

                int a = 7;
                TestMethod(a);
                TestMedhot2(ref a);
                TestMethod3(a);

    这里调用TestMethod3的时候会发生装箱

    image

  • image

     

  • 也就是说,如果仅仅是按引用传递参数的话,那么不会发生装箱和拆箱的问题。也就是说,它并没有产生另外一份数据,而是用指针的方式指向了参数所代表的那份数据而已(这份数据可能在栈上面,也可能在堆上面),但总之是一个指针引用,所以说,按照引用传递的情况,我们如果在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);
            }
    
    
        }
    }
    
  • posted @ 2010-03-19 11:38  陈希章  阅读(633)  评论(0编辑  收藏  举报