深入理解c#函数的参数(值参数,ref,out,params)

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.数组作实参

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


参数类型总结


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容