有这么一道面试题,题目如下:
- using System;
- public class Test1
- {
- public static void Main()
- {
- int num = 0;
- Person p = new Person("Li");
- A1(p, num);
- Console.WriteLine("{0},{1}", p.name, num);
- }
- static void A1(Person p, int num)
- {
- p = new Person("Wang");
- num = 1;
- }
- }
- public class Person
- {
- public string name;
- public Person(string name)
- {
- this.name = name;
- }
- }
说说上面的程序产生的结果,以及产生这个结果的原因是什么?
帖子上面说面试了十个人,居然有十个人答错了,貌似非常不正常啊,但是说白了,这里面主要就是想了解一下面试者对引用传递和值传递的理解;
我在以前过过一篇关于引用传递和值传递的博客,地址如下:http://blog.csdn.net/luxin10/article/details/6195712
今天再就上面的面试题说一说,一方面巩固自己,另一方面方便心里没底的面试者彻底了解;
首先我们得清楚,在C#中,数据类型分为引用类型和值类型,值类型保存在堆栈中,引用类型稍微复杂点,引用类型分为两部分保存,引用类型的值保存在托管堆中,对该值的引用保存在堆栈中,值和值引用构成了一个完整的引用类型变量;我们经常使用下面的语法声明变量:
- int i=0;
- string str = "new string";
在i的声明过程中,系统做了两件事情,一件事情是在内存的堆栈中找到一个4字节的位置(int类型的长度为4字节),转换成代码应该这么表示:int i= new int();第二件事情是将0赋予i,转换成代码应该这么表示:i=0;综合下来实际代码应该如下所示:
- int i=new int();
- i=0;
在str的声明过程中,系统也是做了两件事情,只不过两件事情的手段不一致,第一件事情是在内存中找了两个地方,一个地方在托管堆中,用于存储str的值,另外一个地方在堆栈中,用于指向托管堆的存储位置;转换成代码也是一句话:string str=new string();第二件事也是将new sting这个字符串赋予变量值,但是new string是记录在托管堆中的,这是与值类型的区别,转换成代码应该这么表示:str=“new string”;综合下来代码是一样的,但是在分配内存时存在的差异就比较大了:
- string str=new striing();
- str="new string";
弄清楚了值类型和引用在内存中的保存方式,我们再来看看方法的参数传递,在C#中,所有的参数都是通过值来传递的,被调用的方法得到的都是该值的副本;这里一定要注意:被调用的方法得到的都是该值的副本,也就是说,我们看下面这个方法:
- public void MethDouble(int i)
- {
- return i*2;
- }
下面我们在程序中调用该方法,代码如下:
int i=3;
MethDouble(i);
在以上的代码中,系统做了如下事情:首先将变量在内存堆栈中copy一个副本,这个副本的变量名我们假设称为copy_i,得到副本后,再将副本copy_i传递给方法MethDouble执行;所以在以上的过程中,MethDouble方法内部所做的任何事情都不会对变量i产生任何影响;我们再来看看下面的方法:
- <span style="white-space:pre"> </span>public void StrDouble(string str)
- {
- return str+str;
- }
下面我们在程序中调用该方法,代码如下:
- string str = "myString";
- StrDouble(str);
同理,在以上的代码中,系统做了如下事情:首先将str在堆栈中的值引用变量copy一个副本,这个副本变量名我们假设称为copy_str,这个副本是一个引用,该引用指向的地址就是str引用指向的托管堆的地址,也就是托管堆中“myString”值,得到副本后,再将副本copy_str传递给方法执行,所以在操作过程中,方法对副本所做的修改都会直接修改托管堆中的值,从而影响方法外的str变量;
明白了以上的原理,我们再来看看开篇的面试题:
- int num = 0;
- Person p = new Person("Li");
- A1(p, num);
这里我们可能有疑问了,p的引用传递进去了,那么对p所做的修改应该会影响托管堆中的值啊,我们这里看看方法A1方法的实现
- static void A1(Person p, int num)
- {
- p = new Person("Wang");
- num = 1;
- }
- static void A1(Person person, int num)
- {
- person = new Person("Wang");
- num = 1;
- }
所以面试题的结果应该为:Li,0