引言
参数,也叫参变量,是一个变量。在方法签名中随处可见,实现了不同方法间的数据传递,基本上充斥在代码的各个角落里。
在方法签名或者原型中,方法名称后的括号包含方法的参数及其类型的完整列表。参数声明指定参数中存储的值的类型、大小和标识符。
然而小小参数的背后其实也是有着大大的学问,所以本篇博文,您可以和博主一起把C#里面各式各样的参数复习一遍。
我们先简单回顾一下各种各样的参数概念,对不同类型参数的使用场景有一个了解,再慢慢深入探讨参数的传递,内存堆栈分布,抽丝剥茧,步步为营,带着思考由浅入深的去阅读本文。
形参全称为“形式参数”,由于它不是实际存在变量,所以又称虚拟变量。
形参是在定义方法签名的时候使用的参数,目的是用来接收调用该方法时传递的参数(值),它的作用是实现主调方法与被调方法之间的联系。
形参只在方法内部有效,方法调用结束返回主调用方法后则不能再使用该形参变量。
形参(自身也是变量)和局部变量有所区别,且在方法内部(作用域内)不允许存在一个同名的局部变量,哪怕它们类型是相同的。
//oldValue、parameter1、optionalParam1、optionalParam2就是Change方法的形参 Change<T>(T oldValue, object parameter1, object optionalParam1 = null, object optionalParam2 = null) { T newValue = default(T); oldValue = newValue; }
实参全称为"实际参数",是在调用时传递给方法的参数,即传递给被调用方法的值,形参实际上就是实参的替身。
实参可以是常量、变量、表达式、方法等,无论实参是何种类型的量或值,在进行方法调用时,它们都必须具有确定的值,以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值。
实参和形参在数量上,类型上、顺序上应严格一致,否则就会发生类型不匹配的错误。
Main() { int a = 5; //传进去2个实参,实参1为变量,实参2为值 //实参初始化了形参的初始值 Change(a, 99); }
命名实参
C# 4.0 中引入的命名实参,能够为特定形参指定实参,方法的调用者将不再需要记住或查找形参在所调用方法的形参列表中的顺序,可以按形参名称指定每个实参的形参。
Main() { Change(99, 88); //如果不记得形参的顺序,但却知道其名称,可以按任意顺序发送实参。 Change(oldValue: 99, parameter1: 88); Change(parameter1: 99, oldValue: 88); //命名实参还可以标识每个实参所表示的含义,从而改进代码的可读性。 //命名实参可以放在位置实参后面,如此处所示。 Change(99, parameter1: 88); //但是,位置实参不能放在命名实参后面。 下面的语句会导致编译器报错。 //Change(parameter1: 99, 88); }
如果方法签名的形参比较多,则命名实参技术的使用会使得方法调用变的简便许多。
但是,这种简便性是以牺牲方法签名自由修改的灵活性为代价的。
如果被调用的方法封装在外部dll且不开源,则DLL一旦升级并改变方法的形参名称,则存有依赖的客户端命名实参代码会报错,如下图所示:
可选参数
C# 4.0 中还提供了可选参数,任何调用都必须为所有必需的形参提供实参,但可以为可选的形参省略实参。如果没有为该形参发送实参,则使用定义时的默认值。
每个可选形参都必须有一个初始化的默认值作为其定义的一部分。
可选形参在形参列表的末尾定义,位于任何必需的形参之后。
如果调用方为一系列可选形参中的任意一个形参提供了实参,则它必须为前面的所有可选形参提供实参。