方法的组成 方法一般是由三部分组成。即方法名,参数,方法体。
在下面的代码里
handler – 方法名
c – 参数(可传可不传)
return – 后跟返回值(可返回可不返回)
方法名前面的类型 – 返回值类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 using System;class Test { static public int handler0 () {} static public int handler () { int a = 1 ; int b = 2 ; return a + b; } static public int handler1 (int c = 2 ) { int a = 1 ; int b = 2 ; return a + b + c; } } class Program { static void Main (string [] args ) { Console.WriteLine("Hello, world!" ); Test.handler0(); int res = Test.handler(); Console.WriteLine(res); int res1 = Test.handler1(); Console.WriteLine(res1); } }
方法体 变量 方法里的变量被称为本地变量。存在生命周期。
本地常量 和变量相对应,区别是:只能在声明的时候赋值一次。后面不能改变。
const 要加在类型前面
const 有点像一个修饰符
1 2 3 4 5 关键字 | const int Identifier = Value; | 初始化的值是必须的
块 块中存在生命周期,当块中的代码执行结束后,所有声明的变量都会被销毁。
如果变量在栈里,会被踢出。
一个执行体,可以去嵌套。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Test { static public int handler1 (int c = 2 ) { int a = 1 ; int b = 2 ; { Console.WriteLine("1111: {0}" , a + b); var d = a + b; { Console.WriteLine("1111: {0}" , d); } } return a + b + c; } }
可以使用 var 去声明变量 ,前提是一个可以推断出类型的变量。
c#的静态编译器,从右往左执行代码,同时可以推断出变量的类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 using System;class Test { static public int handler1 (int c = 2 ) { int a = 1 ; int b = 2 ; return a + b + c; } } class Program { static void Main (string [] args ) { var res1 = Test.handler1(); Console.WriteLine(res1); } }
控制流(代码执行顺序)
代码总不是顺序执行的,所以我们需要其他的执行方式。譬如跳转、循环、条件==。
顺序执行
条件执行
if
if…else
switch
循环
for
while
do
foreach
跳转语句
break
continue
goto
return
参数 参数定义 实参 实参类型(取决于参数数据类型) 不同类型的实参,实际产生的效果不同。对于普通整型参数传入时,属于值传入,方法执行的时候会复制一份,不会改变原始值。而参数数组值时,输入引用传入,方法执行传入的是一个引用,方法执行完成,如果有改变形参,则会改变原始值。
形参 形参是本地变量;
调用方法的时候,在代码执行前形参完成初始化;
值参数 值参数取决于参数类型,对于简单类型,那么则是值传递,对于复杂类型则类似下面说道的引用参数。
引用参数 类似于引用传递,会修改原始值。
由于形参名和实参名的行为就好像指向相同的内存位置,所以在方法的执行过程中对形参作 的任何改变在方法完成后依然有效(表现在实参变量上)。
可以强行把简单类型的数值换为引用参数,只需要通过修饰符去修饰即可。
声明和调用中都需要使用ref修饰符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public void fn5 (ref int f1 ) { f1 = f1 + 2 ; } var argsIns = new Args();var refParam = 4 ;Console.WriteLine(refParam); argsIns.fn5(ref refParam); Console.WriteLine(refParam);
输出参数 输出参数用于从方法体内把数据导出到调用代码
声明和调用中都需要使用out修饰符
和返回值毫无关系
可以有多个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Args { public void fn2 (out int x, params int [] args ) { x = 1 ; } } ... int paramOut;argsIns.fn2( out paramOut, paramList); Console.WriteLine("{0}" , paramOut);
参数数组 常规参数
形参,实参一一对应
参数数组的特点
参数无需对应,但是参数数组需要保持相同的类型
通过 params 关键字去修饰为参数列表形式
实参可以传入一个数组类型(如果是数组的话,则类似于引用传递)
实参也可以传入一组数据
1 2 3 4 5 6 7 8 9 static public void fn2 (params int [] args ) { Console.WriteLine(args[0 ]); } int [] arr = {1 ,2 ,3 ,4 };fn2(arr); fn2(1 ,2 ,3 ,4 ,5 ,6 );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class Args { static void Main (params int [] args ) { Console.WriteLine("111" ); } public void fn1 (params int [] args ) { if ((args != null ) && (args.Length != 0 )) { for (int i = 0 ; i < args.Length; i++) { args[i] = args[i] * 10 ; Console.WriteLine("{0}" , args[i]); } } } } class Program { static void Main (string [] args ) { var argsIns = new Args(); int params1 = 1 , params2 = 2 , params3 = 3 ; argsIns.fn1(params1,params2,params3); Console.WriteLine("{0}, {1}, {2}" , params1, params2, params3); Console.WriteLine("=========================" ); int [] paramList = {1 ,2 ,3 }; argsIns.fn1(paramList); foreach (int x in paramList) Console.Write("{0}, " , x); } }
具名参数(named parameter) 在调用方法的时候,可以给参数具名,语法看上更加直观。
可选参数(optional parameter) 参数需具有默认值
如果一个参数具有默认值,那么这个参数可以作为可选参数,可选参数的话,就是这个参数可传递可不传递。
如果存在必传参数,那么可选参数应该放在必传参数之后
方法重载(method overload)
和继承里的方法重写(method override) 不同
一个类中可以有一个以上的方法拥有相同的名称,使用相同名称的方法必须要有一个和其他方法不同的形式,这个叫做签名(signature) 。
方法的签名可以由下列信息组成。
方法名称
参数的数目
参数的数据类型和顺序
参数的修饰符
以上如果不满足任意一条,则重载声明成功,否则失败。
递归 1 2 3 4 5 6 7 8 9 10 11 12 13 static public int Factorial (int Value ) { if (Value <= 1 ) return Value; else return Factorial(Value - 1 ) * Value; } Console.WriteLine(Factorial(4 ));
总结 签名 是重要的,它描述了重载的可能。
C#的方法(函数)玩法是很多的,一个参数都有输入输出,参数列表等。其实在JS中,没有参数输出的概念,其实个人感觉也是很无必要,因为之前用JS也没觉得是必要的。反而参数列表来说,JS是在ECMA2015中实现的。但它不是直接在函数里去扩展,而是通过一个新的语法形式,扩展运算符(spread operator)。从个人理解上来说,参数列表更像是语法糖,而非语法。所以更推崇JS里的实现形式。同时还有一点,在C#的参数列表里,如果方法定义了参数列表,那么实参可以是一个array 也一可以正常的一串参数,这里感觉会有歧义。但在JS里不会,始终是一串参数,只是可以扩展运算符去解释。
具名参数的可玩性很高啊!JS里没有实现这个,哈哈。