《Visual C# 最佳实践》第四章 函数 (二):函数分类
时间:2011-01-22 来源:邹俊才
4.2函数分类
本节将向大家介绍函数的重要组成部分:函数的参数和函数的返回值。同时还向大家介绍函数的传递方法:值传递法和引用传递法,希望对大家有所帮助。
4.2.1函数的参数及传递方式
在函数定义时,函数名后面圆括号内的参数称为形式参数,简称形参。下例中的a、b均为形参。
int sum(int a,int b)
{
……//语句体
}
下面的形参定义是错误的。
int sum(int a, b)
{
……//语句体
}
在函数调用时,函数名后面圆括号内的参数称为实际参数,简称实参。实参可以是常量、已赋值的变量或表达式。实参在次序、类型和个数上应与相应形参表中的形参保持一致。下例中的a、b均为实参。
int a = 10;
int b = 100;
int result = sum(a,b);
对于实参,在调用函数中不仅指明它的类型,而且系统还为其分配存储单元。而对于形参,定义时仅仅只是指明它的类型,并不在内存中为它们分配存储单元。形参只是在调用时才为其分配一个临时存储单元标志,函数执行结束返回调用函数后,该存储单元标志立即撤销。
C#语言中形参与实参之间的传递有如下两种方法。
1.值传递法
使用这种方法时,调用函数将实参(常数、变量、或可计算的表达式)的值传递到被调用函数形参设置的临时变量存储单元中,被调用函数形参值的改变对调用函数的实参没有影响。调用结束后,形参存储单元被释放,实参仍保持原值不变。该方法只能由实参向形参传递数据,即单向传递。
下面我们来看一个范例:
using System;
namespace Microsoft.Example
{
public class TestValueTransfer
{
static int Power(int i, int n) //定义三次方函数
{
int result = i; //定义一个int变量
for (int j = 0; j < n-1; j++)
{
result = result * i; //实现三次方逻辑
}
return result; //返回结果
}
static void Main(string[] args)
{
int i = 20; //定义一个int变量
Console.WriteLine("值传递前,i的值为:" + i);
int result = Power(20, 3); //调用三次方函数
Console.WriteLine("值传递后,i的值为:" + i);
Console.WriteLine("i三次方后的结果是:" + result);
}
}
}
上述代码中,第8行的变量i的初值为20,通过参数传递,调用求i的立方的被调函数Power()。运行被调函数时,实参的复制值被传递给被调函数中的临时变量存储单元i,其值为8000。被调用函数形参值的改变对调用函数的实参没有影响,调用结束后,形参存储单元i被释放,实参i仍保持原值不变,即i=20。
最后的输出结果是:
值传递前,i的值为:20
值传递后,i的值为:20
i三次方后的结果是:8000
2.引用传递法
在值的传递调用中,不能将形参的值返回给实参,但在实际运用中往往需要双向传递,为达此目的,通常使用引用传递。
当实参是数组名或对象名时,实参传递给形参的是地址。若实参是数组名,则调用函数将实参数组的起始地址传递给被调用函数形参的临时变量单元,而不是传递实参数组元素的值。此时,相应的形参可以是形参数组名。在这种传递方法下,被调用函数执行时,形参通过实参传递来的实参数组的起始地址和下标增量,直接去存取相应的数组元素,故形参值的变化实际上是对调用函数的实参数组元素值的改变。
若实参是对象名时,则调用函数将实参对象所指向单元的地址传递给被调用函数形参的临时变量存储单元。此时,相应的形参必须是相同的对象类型。在被调用函数执行时,也是直接去访问相应的单元,形参的变化直接改变调用函数实参相应的单元。
因此,当实参是数组名、对象名时,实参与形参间的传递是双向传递,称“地址的传递”。
(1)数组名作为函数参数的表示方法
当数组名作为函数参数时,也需要对其类型进行相应的说明,例如可以写为:
nt[] values = new int[20];
Test(values);
下面我们来看一个范例:
sing System;
namespace Microsoft.Example
{
public class TestReferTransfer
{
static void Double(int[] values) //定义2倍数函数
{
for (int i = 0; i < values.Length; i++)
{
values[i] = values[i] * 2; //实现2倍逻辑
}
}
static void Main(string[] args)
{
int[] values = { 1,2,3 }; //定义一个int类型的数组
Console.WriteLine("引用传递前,values的值为:");
for (int i = 0; i < values.Length; i++)
{
Console.Write(values[i] + " "); //输出调用函数前的values值
}
Double(values); //调用Double函数
Console.WriteLine();
Console.WriteLine("引用传递后,values的值为:");
for (int i = 0; i < values.Length; i++)
{
Console.Write(values[i] + " "); //输出调用函数后的values值
}
Console.WriteLine();
}
}
}
上述代码中,第15行的变量values是一个数组,通过引用参数传递,调用Double函数,把数组中的所有元素都乘以2。运行被调函数时,数组的引用地址被传递给被调函数中的临时变量存储单元values。被调用函数形参值的改变对调用函数的实参产生了影响,调用结束后,第15行的数组values里面的所有元素都被改变了。
最后的输出结果是:
引用传递前,values的值为:
1 2 3
引用传递后,values的值为:
2 4 6
作者心得:
注意,用数组名作为函数参数时,必须在调用函数与被调用函数中分别定义数组。并且形参数组与实参数组在类型上应该一致,否则会出错。当使用数组作为函数参数时,必须先进行初始化,否则,编译无法通过。
另外,values被定义为具有3个元素的一维整型数组。为了提高函数的通用性,在对形参数组说明时不指定数组的长度,而仅给出类型、数组名和一对方括号,以便允许同一个函数可根据需要来处理不同长度的数组。
(2)为了使程序能了解当前处理的数组的实际长度,往往需要用另一个关键字param来配合使用。
下面我们来看一个范例:
using System;
namespace Microsoft.Example
{
public class TestParamTransfer
{
static void Double(params int[] values) //定义2倍数函数
{
for (int i = 0; i < values.Length; i++)
{
values[i] = values[i] * 2; //实现2倍逻辑
Console.Write(values[i] + " "); //输出结果
}
}
static void Main(string[] args)
{
Double(1, 2, 3, 4, 5); //使用5个参数调用Double函数
Console.WriteLine();
Double(1, 2, 3); //使用3个参数调用Double函数
Console.WriteLine();
}
}
}
上述代码中,第16行我们使用了5个参数来调用Double函数,而第18行我们使用了3个参数来调用Double函数,这样做就很方便。我们在讲数组的时候知道,数组一定要初始化后才能使用,一旦初始化大小就固定了,而且数组不支持修改大小,这样就给我们带来很多的不方便。使用Params关键字后,可以给我们的函数带来更大的方便,更好的重复利用代码,不用5个参数的定义一个方法,3个参数的再定义一个方法。
最后的输出结果是:
2 4 6 8 10
2 4 6
4.2.2函数的返回值
函数调用的目的通常是为了得到一个计算结果或者执行一个操作。为了得到一个结果这个比较容易理解,需要得到结果就肯定需要有地方返回值;那么操作呢,就比较容易忽略。比如,我们需要删除一个文件,那么需要告诉调用者,操作是否成功等相关信息,这个也是需要返回值的。
利用返回语句return可以将计算结果(即返回值)返回给调用函数,同时,也使程序的执行流程转到调用语句的下一语句去执行。
返回语句格式如下:
return(表达式);
或return表达式;
当函数有返回值时,可以在调用函数的地方,得到返回来的数值,并保存在一个变量中。函数的返回值类型一般应当与return语句中的表达式值的类型一致。但是C#语言也允许不同;这时,系统会自动进行隐式类型转换,使其一致。
当函数没有返回值时,函数的返回值类型可以说明为void型,它表示“无类型”或“空类型”。在调用无返回值的函数时,往往是以单独的调用语句出现的。常用的Console.Wrile()函数的调用就是这种形式的函数调用。例如:Console.Wrile(“Hello World!”);只是在屏幕中输出字符串,没有返回值。
下面我们来看一个范例:
using System;
namespace Microsoft.Example
{
public class TestReturnValue
{
static int Max(int x, int y) //定义Max函数
{
int z;
z = x > y ? x : y; //比较x,y的大小
return z;
}
static void Main(string[] args)
{
int x, y, result; //定义3个整型变量
Console.WriteLine("请输入X:");
int.TryParse(Console.ReadLine(), out x); //类型转换
Console.WriteLine("请输入Y:");
int.TryParse(Console.ReadLine(), out y); //类型转换
result = Max(x, y); //调用Max函数
Console.WriteLine("最大的数是:" + result);
}
}
}
上述代码中,第6行定义了Max函数,用来比较两个数的大小,然后把较大的那个值返回函数调用处。Max函数定义了返回类型为int类型,所以ruturn的值也必须是int类型。同时,第19行用来保存返回值的result变量也必须是int类型的。
最后的输出结果是:
请输入X:
1
请输入Y:
3
最大的数是:3