值类型与引用类型在参数传递进程中的测试

值类型与引用类型在参数传递过程中的测试
 1 namespace 4what.program.code
 2     class Student
 3     {
 4         public string Name { get; set; }
 5     }
 6     class Program
 7     {
 8         static void Main(string[] args)
 9         {
10             int i = 15;
11             ChangeValue(i);
12             Console.WriteLine(i);
13  
14             string str = "123";
15             ChangeValue(str);
16             Console.WriteLine(str);
17  
18             Student stu1 = new Student() { Name = "Name" };
19  
20             UpdateStudentNameByProperty(stu1);
21             Console.WriteLine(stu1.Name);
22  
23             Student stu2 = new Student() { Name = "Eman" };
24  
25             UpdateStudentNameByObject(stu2);
26             Console.WriteLine(stu2.Name);
27  
28             Console.Read();
29         }
30  
31         static void ChangeValue(int param)
32         {
33             ++param;
34         }
35         static void ChangeValue(string param)
36         {
37             param = "321";
38         }
39         static void UpdateStudentNameByObject(Student param)
40         {
41             param = new Student() { Name = "Tom" };
42         }
43         static void UpdateStudentNameByProperty(Student param)
44         {
45             param.Name = "Tim";
46         }
47     }
48 }

在看后面的内容之前,请先猜猜上面四个Console.WriteLine()的输出结果都是什么?

值类型与引用类型在参数传递进程中的测试

第一个,没什么好说的,各种教材上都说烂了。值类型作为参数传递的时候编辑的是它的副本。

第二个,有的人会答”321″,其实不是这样的。我们先往后看。

第三个,和第四个比较容易混淆。第三个是对传参传进来的对象赋值,实际上修改了参数的引用。在传参过程中,param拿到了stu1的地址,我们修改了param指针指向的地址,然而并未对stu1地址的对象做任何修改。所以stu1.Name的值还是”Name”。

回头看第二个,现在应该不难理解了吧?

第四个,param是指向stu2在内存中的地址。通过param.Name拿到的就是stu2.Name属性的地址。因此对其修改将直接影响到后面stu2.Name的输出。

现在什么是值类型?什么又是引用类型?

严格意义上说,值类型和引用类型的区别是内存分配方式。值类型在栈上,引用类型在堆上。上述第二个例子非常混淆人的视听。判断数据经过方法调用进行传参并修改后,数据的值是否发生变化的依据是看他在栈上还是堆上。而不是单纯的靠值类型还是引用类型来判断。这一点身边很多的人包括有些教材都对初学者造成了误导。

我们再看下面这道练习题:

namespace net.heavensfeel.code.csharp
{
    class Student
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student() { Name = "Name", Age = 18 };
            ChangeStudentAgeByProperty(stu.Age);
            Console.WriteLine(stu.Age);
        }
        static void ChangeStudentAgeByProperty(int param)
        {
            ++param;
        }
    }
}

现在好好想想Console.WriteLine()方法输出的值是多少?这里就不贴图了。想知道答案自己敲一遍运行一下就知道了。一定要想清楚为什么。

我在上初中的时候,数学老师每讲完一道题,都会给我们一段时间让我们给同桌再互相讲一遍。如果某些细节上模棱两可,就会讲到一半就讲不下去了。似懂非懂不如不懂。我们以为我们已经掌握的知识,只有在用的时候,在说出来的时候才能看出掌握的是否牢固。编程也是这样,经常我们会遇到差不多是这样,那样又行得通的约定。经过关哥的调教决定以后对这些细节一定要想清楚为什么然后记下来。虽然很难在实际工作中用到,但探索的过程很过瘾不是么!

在实际的开发过程中,还要避免写这种混淆视听的代码,来提高代码的可读性。减少在交流上造成的困扰。