目录
一.基本定义:
类中函数,称方法,又称行为;
1.方法的基本组成:
(1)方法修饰符:public 和 static;
(2)返回值:void没有返回值,有返回值的需要配合return;
(3)方法名称:命名规则:大驼峰;
(4)形参列表:形参就是形式参数,将来用实参来传递形参;()中就是方法形参, 形参不固定。
(5)方法体 :在{}中;
[public] [static] 返回值类型 方法名字([参数列表])
{
方法体
}
2.方法的基本分类;
静态方法、实例方法、无参方法、有参方法
静态方法:使用static,只存于类本身,只能通过类本身访问,不能通过实例访问即个体实 现;如:
public static void Add(int a,int b){}
实例方法:没有使用static 修饰符修饰的方法,,必须通过类的个体实现;
无参方法:就是()中没有值;
public void Add(){}
有参方法:就是()中有值;
public void Add(int a,int b){}
二.方法的使用(设计与调用)
1.方法:当返回值为void,不需要返回值即return;
public void Add(int a,int b)
{
result = a + b;
Console.WriteLine(result);
}
输出:此时只需传参就可以调用方法
var clac=new Clac();//实例化
clac.Add(10,20);//调用方法,Add(10,20)是传参
//在本类中调用,不需实例化,直接调用方法即可
2.当返回值为基本数据类型时,需要return;
public float Add(float a,float b) {
return a + b;
}
输出:因为此时有返回值,所以需要输出打印
var res = clac.Add(12.5f,25.3f);
Console.WriteLine(res);
//也可以简写
Console.WriteLine(clac.Add(12.5f, 25.3f));
3.一个方法返回值的类型可以和形参的类型不一样;
public string Add(float a, float b,float c)
{
return (a+b+c).ToString();//TOstring 转字符串类型
}
三.var(variable变量)与命名参数
var:
在C#中,原来定义变量需要基本数据类型,现在C#中提供一个关键字var(variable变量),专门用来定义变量,它定义的变量不需要指定的类型,变量会根据变量值进行【类型推断】,如:
var a = 10;
命名参数:
方法的实参和形参类型要保持高度一致,传递顺序也要高度一致;如果开发者传递实参实时,没有和形参保持一致,又想让代码执行成功,怎么办?
答:给实参起别名;命名参数可以让开发者给实参指定传递位置
public static int Add(int a,int b)
{
Console.WriteLine($"a的值:{a},b的值{b},结果{a+b}");
return a + b;
}
//输出
Clc.Add(20,10);
Clc.Add(b:20,a:10);
//输出结果:a的值:20,b的值10,结果30
// a的值:10,b的值20,结果30
四.方法中变量作用域
作用域是在指当前方法内,比如想让一个变量在多个方法中使用,那么就需要把这个变量的作用域扩大,如何扩大? 可以将其定义到类中(注意:此时的变量与方法中的变量虽然一样,但是却不是同一个)。如:
public class Calc
{
int result;//扩展到类范围
public int Add(int a, int b)
result = a + b;//此变量和外部变量return不是同一个
return result;
}
public void Fun1()
{
//result变量必须在当前上下文中定义才能使用
Console.WriteLine(result);//想要使用result 就要扩展扩展,此处调用的是外部return
}
}
//调用
var clc = new Calc();
clc.Fun1(); //结果: 0 此时return还没有返回外部return
var res= clc.Add(10, 20);
Console.WriteLine(res); //结果: 30
clc.Fun1(); //结果: 30
五.参数的关键字
1.params参数(可变参数)
可变参数语法要求:
1.使用patams关键字来标识此参数是可变参数
2.可变参数必须是方法的最后一个形参;
3.可变参数不是说类型可变,而是指个数可变
public void Add2(params int[] arr)
{
Console.WriteLine($"索引{arr[0]},索引{arr[1]},索引{arr[2]},索引{arr[3]}");
}
//输出:
MyClass myClass = new MyClass(); //实例
var arr = new int[] { 0,1, 2, 3 }; //给形参数组传参
myClass.Add2(arr);
2.out参数(引用传递)
- 可以帮助我们在一个方法中返回多个值,不限类型。
- 使用out参数的时候,要求out参数必须在方法内为其赋值,也就是在方法内进行初始化,即使赋值了也会被清空,out也是引用传递,
- out用法:一般用于需要返回多个参数时,如在需要返回分页的数据时 同时返回总条数或者TryParse()会用到
public void Add3(int a,out int b)
{
b = a + 10;
//之前b想要在调用Add3的时候得到b,需要借助return;
//现在也可以借助out转出参数,实现和return相同的效果
}
//输出:
int res3 = 0; //先定义初始值,
myClass.Add3(1,out res3);//进入方法体,得到b为11,在输出初来给res3
Console.WriteLine(res3);
3.ref参数(引用传递)
ref用法:一般用于在改变一个参数时,把他的改变反应到变量中 如在递归中
在方法中修改ref参数会影响外部变量
public void Add4(int a,ref int b)
{
a = 20;
b = 10;
Console.WriteLine($"内部a的值{a},內部b的值{b}");
}
//输出:
a = 200; //先传到形参
int b = 100;//先传到形参
myClass.Add4(a,ref b);
Console.WriteLine($"外部a的值{a},外部b的值{b}");
//结果:
//内部a的值20,內部b的值10
//外部a的值200,外部b的值10
总结:ref和out值必须是可以赋值的变量,也就是说需要进行初始化
六.值传递和引用传递
值传递:方法中修改了形参,不影响外部的实参;
引用传递:方法中修改了形参,影响外部的实参;
值传递有哪些?
答:1.基本数据类型都属于值传递
2.string类型的值传递:
直接使用string类型传递,也没有借用类调用 ,相当于重新定义参数,会生成新的内存空间,所以实参地址不会改变;也即字符串会产生新的地址;
假设实参为name,x形参name2;那么开始时实参传给形参即:name2=name(地址一样),
但在方法中又对name2="小白"即产生新的地址,故而name2的地址也随改变
引用传递有哪些?
答:1.数据类型前面加 关键字ref ;据类型前有ref表示传递的数据是引用类型,是地址;
2.string类型的引用传递:
借用类的实例,修改属性时没有生成新的内训地址
在类加载期间,将字符串添加到字符串常量池中,再添加字符串会判断字符串内容是否存在池中,若存在便不会分配内存空间,故而会导致地址的改变。
string str=null;为空是不会分配内存空间的;
string str=" ";表示占用了一个位置,因此会分配地址
七.方法的调用顺序
1.调用多个方法,方法直接没有联系时
1.代码执行顺序,默认从上到下()按调用顺序,先执行Fun1,在执行Fun2;
2.当代码遇见开发者封装方法时,会进入方法体执行,之星结束,返回到调用方法处;如果代码遇见的不是开发者封装的方法,而是C#内置的类库提供的方法,这时候并不会进入到方法体。
原因:类库提供的方法源码是隐藏的;
var class1 = new Class1();
class1.Fun1(); //Class1中的Fun1()中,方法体执行完后,会返回到此行代码;
class1.Fun2();//Fun1.Fun2是方法名
2.调用多个方法,方法有联系时(嵌套)
//方法嵌套调用 这几个方法之间存在联系
public void Fun3()
{
Console.WriteLine("我是fun3");
Console.WriteLine("我是fun3.1");
Fun4();
}
public void Fun4()
{
Console.WriteLine("我是fun4");
Console.WriteLine("我是fun4.1");
Fun5();
}
public void Fun5()
{
Console.WriteLine("我是fun5");
Console.WriteLine("我是fun5.1");
}
//调用:
class1.Fun3();
//结果:我是fun3
//我是fun3.1
//我是fun4
//我是fun4.1
//我是fun5
//我是fun5.1
注意:重点来了
看完输出结果,此时会不会觉得调用顺序是先把Fun3执行完,再执行Fun4,再执行Fun5错误顺序),其实不然。
可以通过断点判断出真正的顺序:F3(没执行完)-F4(没执行完)-F5(一次性执行完)-F4(完)-F3(完)
进入的顺序:方法嵌套的顺序F3-F4-F5; 执行的顺序:F3-F4-F5 执行完毕顺序:F5-F4-F);先进后出
八.方法的栈帧
方法调用的栈帧:就是执行方法时,给方法分配的内存空间;
方法执行时,只要分配了内存空间,调用方法,会进入此内存空间。内存空间执行顺序就叫做“调用栈”;
调用栈其实就就是先进后出,这个栈帧的执行顺序我称之为:是一个U型结构;
九.方法的递归调用
在一个方法中,调用方法本身,简言之就是:自己调用自己
递归拥有一个自循环。递归要小心地是:一定要有出口(方法可以停止)。 递归没有出口,就会进入“死循环”,出现栈溢出的错误;
代码演示:
private int count = 3;
public void Count() {
Console.WriteLine($"当前是:{count}");
if (count-- == 0)
{
return;
}
Count();//调用自己
}
//调用:
class1.Count();
/*输出结果:当前是:3
当前是:2
当前是:1
当前是:0 */
十.斐波那契数列(Fibonacci sequence)
斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)
代码演示:
public int Fib(int n)
{
if (n == 0)
{
return 0;
}
else if (n == 1)
{
return 1;
}
else
{
return Fib(n - 1)+Fib(n-2);
}
}
//输出:
Console.WriteLine(class1.Fib(0));
Console.WriteLine(class1.Fib(1));
Console.WriteLine(class1.Fib(2));
Console.WriteLine(class1.Fib(3));
Console.WriteLine(class1.Fib(4));
Console.WriteLine(class1.Fib(5));
//结果:0 1 1 2 3 5