文章目录
4. 函数
4.1 函数基础
什么是函数?
-
函数又称方法,本质是一个具有名称的代码块。
-
可以通过函数的名称去执行该代码块。
-
函数是方便对代码重复使用的一种机制。
为什么要使用函数?
- 它可以将一系列相关语句封装起来。
- 可以重复调用,减少冗余代码的书写。
- 代表类中的一些抽象行为,如实现简单的吃饭、睡觉方法行为。
函数基本语法是什么?
一个函数的基本形式为:
<访问修饰符> <其他修饰符> <返回值类型> <函数名>(<参数类型1> <参数名1>,..., <参数类型n> <参数名n>)
{
代码逻辑1;
...
代码逻辑n;
return <返回值>;
}
对于每一部分解释如下:
- 访问修饰符:访问修饰符作用为设置该函数对其他类的可用性,常见的有
public(对所有类可用)
、private(仅自己所在类可用)
、protected(对自己和子类可用)
,具体见C# 封装 | 菜鸟教程 (runoob.com)|访问修饰符 - C# reference | Microsoft Learn。 - 其他修饰符:可选内容,如
static(静态方法)
、virtual(虚方法)
等,参考abstract 关键字| Microsoft Learn|override 修饰符| Microsoft Learn|static 修饰符| Microsoft Learn|virtual 关键字| Microsoft Learn|async 关键字| Microsoft Learn。 - 返回值类型:返回值类型包含
void(无返回值类型)
和其他所有数据类型(如int
、float
、自定义类等等),他表示这个函数执行完毕后将会返回的数据类型。 - 函数名:该函数调用时所使用的标识名称,一般采用大驼峰命名法。
- 参数列表:数量可选,类型可以是任何数据类型,表明执行该函数所需要的参数类型及数量。
- 代码逻辑:函数中的处理逻辑。
- 返回值:在执行结束后,对于非
void
返回值类型函数,必须返回一个对应类型的数据,对于void
,可以不写return
语句,也可以写上return
后直接加分号结束而不返回任何值。
例如,我们书写一个返回值为int
的两个整数相加的方法函数AddTwoNum
:
/// <summary>
/// 两整数相加获取结果
/// </summary>
/// <param name="num1">第一个整数</param>
/// <param name="num2">第二个整数</param>
/// <returns>两数之和</returns>
public int AddTwoNum(int num1, int num2)
{
int res = num1 + num2;//计算结果
return res;//返回结果
}
对于上面的函数,因为它的逻辑比较简单,可以直接将里面的语句缩短为一句,即return num1 + num2;
,这样可以省去函数调用时对于结果数据申请的额外空间。
如何进行函数调用?
对于同一个类或结构体中的函数,我们可以直接通过函数名进行调用,例如调用上面的AddTwoNum
函数计算1+2的结果:
int res = AddTwoNum(1, 2);
对于不同类的非静态函数,需要通过对应类的实例对象进行调用,如对People
类中的void SayHello()
进行调用,需要先创建该类的对象,再进行函数调用:
People alice = new People;//创建实例化对象
alice.SayHello();//调用函数
对于不同类的静态函数,可以直接通过对应类名进行调用,如调用Calculator
类中的public static int Add(int num1, int num2)
:
int res = Calculator.Add(1, 2);//直接调用获取结果
4.2 ref
和out
关键字
什么是ref
和out
?
- ref:
ref
用于添加在参数类型前,它可以将传入的参数作为引用类型使用,使用时必须在传入参数前加上ref
关键字。 - out:
out
同样用于添加到参数类型前,添加了out
关键字的参数可以作为函数内部所需参数使用,也可以仅作为返回值的接收对象。
为什么要使用ref
和out
?
在实际编码中,我们可能需要同时使用多个变量参数并希望他的值随着函数运行而修改,但普通值类型变量传参时函数内对该参数进行修改并不会引起原来变量的数值产生任何变化,通过ref
可以让他像引用类型一样进行函数内值的修改。
而面对函数有多个返回值的情况,使用out
关键字可以解决原本函数只能有一个返回值的问题,他让我们的函数功能性更加强大。
它们的区别是什么?
ref
参数必须要进行初始化,out
可以不进行初始化ref
参数在函数内部可以不做任何处理,out
参数必须要进行赋值
参考链接:
ref 关键字 - C# reference | Microsoft Learn
out 关键字 - C# reference | Microsoft Learn
如何进行使用?
通过ref
关键字将值类型变为引用类型处理:
public void NumAddOne(ref int num)
{
num++;
}
//调用
int num = 0;
NumAddOne(ref num);//调用结束后原本的值+1
使用out
关键字实现多值返回:
//获取num的平方和开方
public void Num(int num,out int pow2,out double sqt)
{
pow2 = (int)Math.Pow(num, 2);
sqt = Math.Sqrt(num);
}
//调用函数
int num = 4;
Num(num,out int pow,out double sqt);
Console.WriteLine($"{
num}的平方为{
pow},开方为{
sqt}");//4的平方为16,开方为2
4.3 变长参数params
什么是变长参数?
- 变长参数函数的传入参数,他可以为任意数量的同种类型参数的集合。
- 关键字为
params
,使用时后面必须修饰一个数组而非一个元素。 params
关键字修饰的参数数组必须位于最后一个参数的位置,在她的后面不允许有其他任何参数。
为什么要使用变长参数?
在做一些处理时,我们可能并不知道实际需要处理的参数数量,这种情况下无法直接在声明时定义下来对应的参数,使用变长参数则可以解决这个问题。
如何使用变长参数?
语法为params <数据类型>[] arr
,例如获取一系列整数之和:
public int SumOfNum(params int[] arr)
{
int sum = 0;
for (int i = 0; i < arr.Length; i++)
{
sum += arr[i];
}
return sum;
}
调用时可以传入任意数量的整数,如:
int sum = SumOfNum(1, 2, 3, 4, 5, 65);//sum = 80
也可以直接传入数组:
int[] arr = {
1, 2, 3, 4 };
sum = SumOfNum(arr);//sum = 10
参考链接:方法参数 - C# reference | Microsoft Learn
4.4 可选参数/参数默认值
什么是可选参数?
- 在实际应用中,存在某些情况下逻辑处理必须要用到某参数,但实际调用时不一定会包含该参数的传入,这时可以使用可选参数。
- 可选参数含有一个默认值,在没有传入参数时将会直接使用该默认值,有参数传入时则以世纪传入数据为准。
- 书写可选参数是直接在参数声明时赋值即可。
- 可选参数之后不能有任何普通参数声明,但可以为变长参数或其他可选参数。
为什么需要可选参数?
函数编码时,存在一些情况下使用函数者可以对某一些参数进行忽略的情况,例如在求圆的面积的时候,我们默认会写上一个较为精细的圆周率参数,而使用者有时候不会需要这么精细的结果,这是他可以选择直接修改传入圆周率参数或将结果自行进行修改。
如何进行使用?使用举例
声明可选参数即在参数列表进行赋值,如下函数所示:
public void Say(string name, string str = "无话可说")
{
Console.WriteLine(name + str);
}
调用时不进行可传参数传递:
Say("Alice");//Alice无话可说
调用时传入具体参数:
Say("Alice", "并非无话可说");//Alice并非无话可说
4.5 递归函数
什么是递归函数?
递归函数简单来说就是在自己函数内部调用自己本身的函数。
使用递归函数的注意事项?
- 递归函数必须要有停止条件。
- 函数的停止条件应该为一个可达成的条件,否则将会导致无限递归最终内存溢出。
- 递归函数书写简单,但是对内存和性能消耗非常大。
如何使用递归函数?使用举例
用递归函数实现求取斐波那契数列中第n个数的值:
public int Fibonacci(int num)
{
if (num == 1 || num == 2) return 1;
else if (num > 2) return Fibonacci(num - 1) + Fibonacci(num - 2);
else return 0;
}
Console.WriteLine(Fibonacci(10));//输出结果为55(1 1 2 3 5 8 13 21 34 55)