C# 之 方法参数传递机制
根据参数传递机制的不同,C#方法的形式参数分为四种:值形参、引用形参、输出形参以及形参数组,通过在形参定义时添加不同的参数描述符来表示。
一、值形参:(值类型形式参数)
声明时不带修饰符的形参是值形参。
一个值形参对应于方法的一个局部变量,只是它的初始值来自该方法调用所提供的相应实参。
当形参是值形参时,要求方法调用中的对应实参必须可以隐式转换为形参的类型。
看一个交换数的例子,当执行 Exchange 方法,Main 中的 a 和 b 并没有收到影响:
class Program
{
static void Main(string[] args)
{
int a = 3, b = 5;
Console.WriteLine("Main.. a:" + a + "...b:" + b);
Exchange(a, b);
Console.WriteLine("Main.. a:" + a + "...b:" + b);
}
static void Exchange(int a, int b)
{
Console.WriteLine("Exchange.. a:" + a + "...b:" + b);
int temp = a;
a = b;
b = temp;
Console.WriteLine("Exchange.. a:" + a + "...b:" + b);
}
}
复制代码
二、引用形参(ref 关键字)
用 ref 修饰符声明的形参是引用形参。
引用形参所表示的存储位置就是方法调用时给出的那个实参的存储位置。
当形参为引用形参时,方法调用中的对应实参必须由关键字 ref 并后接一个与形参类型相同的变量组成。变量在可以作为引用形参传递之前,必须先明确赋值。
同样是上面示例中的代码我将方法参数使用 ref 修饰,看下结果:此时的 Main 方法中的 a 和 b 的值也发生了转换.
class Program
{
static void Main(string[] args)
{
int a = 3, b = 5;
Console.WriteLine("Main.. a:" + a + "...b:" + b);
Exchange(ref a,ref b);
Console.WriteLine("Main.. a:" + a + "...b:" + b);
Console.ReadKey();
}
static void Exchange(ref int a,ref int b)
{
Console.WriteLine("Exchange.. a:" + a + "...b:" + b);
int temp = a;
a = b;
b = temp;
Console.WriteLine("Exchange.. a:" + a + "...b:" + b);
}
}
复制代码
也就是说,当我们使用引用形参时,多个名称会表示同一存储位置。
三、输出形参 (out 关键字)
用 out 修饰符声明的形参是输出形参。
输出形参所表示的存储位置就是在该方法调用中作为实参给出的那个变量所表示的存储位置。
使用方式和引用参数差不多一个使用 ref 关键字修饰,一个使用 out 关键字修饰,区别在于引用参数需要在方法调用前赋值,而输出参数在方法调用后,返回之前赋值
举个栗子:
class Program
{
static void Main(string[] args)
{
int[] arr = new int[5] { 1, 2, 3, 4, 5 };
float average;
int sum = Calculation(arr, out average);
Console.WriteLine("这组数的和是:" + sum + "..平均数是:" + average);
Console.ReadKey();
}
static int Calculation(int[] arr,out float average)
{
int sum = 0;
for (int i = 0; i < arr.Length; i++)
{
sum += arr[i];
}
average = sum / arr.Length;
return sum;
}
}
复制代码
输出参数可用于从方法向调用者传递数据。
四、形参数组 (引用类型形式参数)
当方法参数传递的类型为引用类型时,对参数中的值进行修改,就是对原变量进行修改,也就是说引用类型传递参数时,传递进去的是地址.
举个栗子:修改参数数组中的值,原数组也会被修改,看代码:
class Program
{
static void Main(string[] args)
{
int[] arr = new int[5] { 1, 2, 3, 4, 5 };
Console.WriteLine("Main 调用方法前:");
for (int i = 0; i < arr.Length; i++)
{
Console.Write(arr[i] + " ");
}
Console.WriteLine();
Reference(arr);
Console.WriteLine();
Console.WriteLine("Main 调用方法后:");
for (int i = 0; i < arr.Length; i++)
{
Console.Write(arr[i] + " ");
}
Console.ReadKey();
}
static void Reference(int[] arr)
{
Console.WriteLine("Reference 传进来:");
for (int i = 0; i < arr.Length; i++)
{
Console.Write(arr[i] + " ");
}
for (int i = 0; i < arr.Length; i++)
{
arr[i] = 0;
}
Console.WriteLine();
Console.WriteLine("Reference 修改后:");
for (int i = 0; i < arr.Length; i++)
{
Console.Write(arr[i] + " ");
}
}
}
复制代码
C#中的位置参数和命名参数
位置参数:进行方法调用时,每个实参的位置都必须一一对应相应形参的位置,这种参数叫做位置参数,我们上面例子中使用的都是位置参数。
从 C#4.0 开始,方法调用时支持命名参数机制,只要显式指定参数的名称,就可以以任意顺序在方法调用中列出实参。
我将 1 例的代码修改了一下调用的形式,大概是这样:
class Program
{
static void Main(string[] args)
{
int a = 3, b = 5;
Console.WriteLine("Main.. a:" + a + "...b:" + b);
//Exchange(a, b);
Exchange(b: b, a: a);
Console.WriteLine("Main.. a:" + a + "...b:" + b);
Console.ReadKey();
}
static void Exchange(int a,int b)
{
Console.WriteLine("Exchange.. a:" + a + "...b:" + b);
int temp = a;
a = b;
b = temp;
Console.WriteLine("Exchange.. a:" + a + "...b:" + b);
}
}
复制代码
可能这个例子写的不太好,不应该将形参和实际变量名起重复的名字,不过这个并影响功能,由下图可以看出当我选中形参中的 a 时,方法中的 a 都会被标识出来,表示是同一个变量,此时 Main 方法中的 a:a 也被标识出来了,那么说明他和这个形参也是同一变量
PS:命名参数,可以明确知道是那个变量赋值个了那个参数,这使得方法调用时具有更多的参数时不容易出错,增加代码的可读性。
评论