c#函数参数有四种类型,分别是:值参数、引用参数、输出参数、参数数组
在了解函数的参数之前,我们先来了解什么是实参和形参。
什么是形参?
形参是声明在方法的参数列表中,他可以是不同类型的,中间用逗号隔开

什么是实参?
当调用一个方法时,用于初始化形参的表达式或变量成为实参,他放在方法调用的参数列表中(注:他可以是表达式)

调用方法时,必须满足以下要求:
● 实参的数目必须和形参数目相同(参数数组除外)
●每个实参必须和相应的形参类型匹配
1.值参数
什么是值参数?
通俗的讲,没有用到ref,out,param等修饰符,形式是类型+参数名的参数。
书上解释:有几种参数,他们使用稍微不同的方法从方法传入或传出数据。你到现在一直看到的这种类型是默认的类型,成为值参数
特点:
使用值参数,数据通过复制到实参的值到形参的方式传递到方法。方法被调用时,系统做如下操作:
● 在栈中为形参分配空间。
● 复制实参到形参
下面来看这样一个实例


问题:为什么在MyFunc方法之f1和f2同样加了5,但是a1的val值变了,a2的值却没有变?

我们通过堆栈来分析:
1.在方法被调用前,用作实参的变量a1和a2都已经在栈里了。
2.随着方法的开始,系统在栈中为形参分配空间,并把实参的值复制给形参。
● 因为a1是引用类型的,所以引用被复制,所以实参和形参都引用堆中的唯一对象(f1和a1)。
● 因为a2是值类型的,所以值被复制,产生了一个独立的数据。
3.在方法的结尾,f2和对象f1的字段都被加上了5。
4.方法执行后,形参从栈中弹出。
●a2,值类型,他的值没有受方法的影响。
●a1,引用类型,因为他和形参f1指向同一块地址,所以随着f1改变,他的值一 块跟着改变了。
2.引用参数ref
什么是引用参数?
通俗的讲,在参数类型前加上ref修饰符的参数
特点:
● 必须在方法声明和调用中都使用ref修饰符
● 实参必须是变量,在用作实参前必须被赋值,否则会报错,如果是引用类型变量,可以赋值为一个应用或者null
● 和值参数不同,不在栈中为形参分配新的内存
●形参的名称相当于实参变量的别名,引用于实参相同的内存位置


下面来看这样一个实例

问题:为什么这次a1和a2的值最终改变了?
同样的,我们通过堆栈来分析

1.在方法调用之前,实参变量a1和a2都已经在栈里了
2. 在方法开始之前。形参名被设置为实参的别名。变量a1和f1都指向了堆中相同的内存地址,f2和a2都使用了栈中同一块内存
3.在方法结束位置,f2和f1都加上了5
4.方法结束后,形参的名称已经失效,但是a2的值和a1指向的对象的值都在方法内部被改变了。a2是值类型的变量,a1是引用类型
3.输出参数out
什么是输出参数?
通俗的讲,在参数面前加上out修饰符的参数
输出参数用于从方法体内吧数据传出到调用代码。
特点:
● 必须在声明的调用中都使用out修饰符
● 实参必须是变量,而不能是其他表达式
● 形参相当于实参的别名,形参和实参都是统一内存位置的名称
与ref参数的不同点
● 在方法内部,输入参数在被读取之前必须被赋值。这意味着与参数的初始值是无关的,所以没有必要在方法调用之前为实参赋值。
● 每个输出参数在方法返回之前必须被赋值,否则会被报错

下面来看这样一个示例

可以看到,和引用参数一样,a1 a2的值最后都变了
我们通过堆栈来分析

1. 在方法调用之前,将要被用作实参的变量a1和a2都已经在栈里了。
2.在方法的开始,形参名被设置为实参的别名,变量a1和f1都指向了堆中相同的内存地 址,f2和a2都使用了栈中同一块内存
3.在方法内部,创建了一个MyClass类型的对象并把它赋值给f1,然后赋一个值给f1的字段,也赋一个值给f2。对f1和f1赋值时必须的,因为他们是输出参数
4.方法结束后,形参的名称已经失效,但是a2的值和a1指向的对象的值都在方法内部被改变了。
4.参数数组params
什么是参数数组?
简单的说就是 params + 参数类型 + [ ] + 参数名 的参数形式,它允许多个或者0个实参对应一个特殊的形参。
特点:
● 在一个参数列表中只能有一个参数数组。
● 如果有,他必须是列表中的最后一个。
● 在数据类型前面用params修饰符
● 在数据类型后放置一组空的方括号
● 实参可以为0


如何调用?
● 用一个逗号分割该数据类型元素的列表。所有元素必须是方法声明中指定的类型。
● 一个该数据类型元素的一维数组

1.数组分离作实参

可以看到虽然在方法内部修改了参数的值,但是打印出来实参的值并没有改变
用堆栈解释

● 在方法调用之前,三个实参就已经在栈里
● 在方法开始的时候,在栈中新加入形参inVals,三个实参被用于初始化堆中的数组,在并且数组的引用被赋值给形参inVals
● 在方法内部,代码首先检查以确认数组医用不是null,然后处理数组,把每个元素乘10并保存回去
● 方法执行之后,形参invals失效
注意:
● 如果数组参数是值类型,那么值被复制,实参不收方法内部的影响
● 如果数组参数是引用类型,那么引用被复制,实参引用的对象可以受到方法内部的影响

2.数组作实参
把单一的数组变量作为实参传递,这种情况下,编译器使用你的数组而不是重新创建一个

参数类型总结
