问题的来源
这个问题很有意思,当时是我一个学Java的室友问我C#默认是用值参数传递还是引用参数传递的。因为我很久没有看C#基础知识了,以为当形参是值类型时就采用值传递,是引用类型时就采用引用传递。然后室友继续问,值传递和引用传递有什么区别?我说很久没看了有点忘了。然后今天特意去翻了下书,不得不说,《C#图解教程》这本书里讲的还是很通俗易懂的。
第一个问题,C#默认的方法参数传递类型?
值传递,兄弟们记住了,与Java一样,都是值传递!
第二个问题,值传递与引用传递的区别
首先,我们来看下什么是值传递。值传递就是调用方法时,栈会自动给这个形参分配一个空间,然后将实参的值复制给形参。这里不要与值类型混淆,虽然这个过程跟值类型变量的赋值过程极其相似,但无论这个形参是值类型还是引用类型,采用的都是“复制”的机制。这本书里举了个非常好的例子:
class MyClass
{
public int Val = 20;
}
class Program
{
static void MyMethod(MyClass f1,int f2)
{
f1.Val = f1.Val + 5;
f2 = f2 + 5;
Console.WriteLine($"f1.val:{f1.Val},f2.val:{f2}");
}
static void Main()
{
MyClass a1 = new MyClass();
int a2 = 10;
MyMethod(a1, a2);
Console.WriteLine($"f1.val:{a1.Val},f2.val:{a2}");
}
}
这段代码会产生如下结果:
f1.val:25,f2:15
f1.val:25,f2:10
- 在方法被调用前,用作实参的变量a2已经在栈里了
- 在方法开始前,系统在栈中为形参分配空间,并从实参复制值
2.1 因为a1是引用类型的,所以引用被复制,结果实参和形参都引用堆中的同一个对象
2.2 因为a2是值类型的,所以值被复制,产生了一个独立的数据项 - 在方法的结尾,f2和对象f1的字段都被加上了5
3.1 方法执行后,形参从栈中弹出
3.2 a2,值类型,它的值不受方法行为的影响
3.3 a1,引用类型,但它的值被方法的行为改变了。
具体过程如下图所示:
然后我们来看看引用参数传递又是怎么一回事。使用引用参数时,必须在方法的声明和调用中都使用ref修饰符。当实参传递给形参值时,系统栈并不会给形参的变量分配一个空间,而是将形参变量作为实参变量的别名,他们都指向同一对象。如图所示:
与上图一样的示例代码,只是添加了ref关键字,然后我懒得敲了,就直接拍照上传了。
其过程图如下所示: