最近换工作面试,遇到了一个基础的坑,是关于C#参数传递的问题。
参数传递主要分两种,值传递和引用传递。实际C#还提供的out输出传递和params数组传递,ref和out这两个关键字都能够提供相似的功效,都能实现引用传递。
I. 使用ref传递参数时,传入的参数必须先被初始化。对out而言,必须在方法中对其完成初始化。
II. 使用ref和out时,在方法声明的参数和调用方法时,都要加ref或out关键字。
III. out适用于return多个返回值,而ref用来改变调用者的引用。
params数组传递,提供同一种类型的多参数传递的方案。
1、值传递
(1)值类型
示例代码
---------------Main-----------------
int a=1;
circle(a);
---------------circle-----------------
void circle(int r)
{
r=10;
}
如下图,值类型存储在栈内存空间上,在被调用方法里修改的是另外一块内存空间的值。
(2)引用类型
示例代码
---------------Test-----------------
class Test
{
public int A {get; set;}
}
---------------Main-----------------
var test=new Test();
test.A=1;
Set(test);
---------------Set1-----------------
void Set(Test t)
{
t.A=10;
}
如下图,test和t两个变量指向的是同一个实例,所以当t去修改A属性的时候,实际上是修改了test指向的实例。
---------------Set2-----------------
void Set(Test t)
{
t=new Test();
t.A=10;
}
这种情况需要注意,一开始test和t都指向同一个实例。在Set方法内部对t重新分配另外一块内存空间,接下来t的任何操作都在另外一块内存空间上,跟test没有关系。
2、引用传递
(1)值类型
示例代码
---------------Main-----------------
int a=1;
circle(ref a);
---------------circle-----------------
void circle(ref int r)
{
r=10;
}
实际上,a和r指向的是同一块内存空间,r修改了,实际上就是修改了a。
(2)引用类型
示例代码
---------------Test-----------------
class Test
{
public int A {get; set;}
}
---------------Main-----------------
var test=new Test();
test.A=1;
Set(ref test);
---------------Set1-----------------
void Set(ref Test t)
{
t=new Test();
t.A=10;
}
test传入的是指针,所以当指针指向另外一个实例时,t和test指向的就是该指针指向的实例,它们操作的都是这个实例对应的内存。
总结
(1)C#的值类型存储在栈上,引用类型却存储在CLR托管堆中。
(2)ref传递的是参数的引用。