@TOC
唠唠嗑
嗨,我是小白,如果你不喜欢看一整本书而是带着轻松的心情看别人学习一本书,或者喜欢通过和人讨论的模式来进行学习,就来关注我吧。 如果有比我还小白的小白们有非常非常基础的问题要问,我会根据私信人数出期特别的,简单易懂的教程哦。
从本期开始将按每章的每小节做一个知识总结。 因为有很多过于基础的内容所以可能一小节也会一句话带过。 明天就要拿材料办入职了,要开始工作了,有点小激动啊,如果有很棒的学习资源那可就太好了ლ(´ڡ`ლ).
总目录
本文章阅读目录
一、略过的小节
把过于基础的小节内容略过,如果有需要本书资源的可以私信我哦。
5.1 方法的结构
主要讲了一下方法的语法,方法头的结构(如void Abc()),方法体的结构(跟在方法头后边的大括号)。
5.2 方法体内部的代码执行
讲了下方法体内部都可能有什么,就是你的语句,什么Int a = 1;调用方法的语句,还有for循环什么的。
5.4 本地常量
和变量不一样的就是这个在声明初始化后值就不可更改了。 声明方式 const 类型 变量名 = ... (const必须带上,右边也必须赋值进行初始化。)
5.5 控制流
就是if-else,switch,for这种控制程序运行流程的判断和循环语句,还有break,goto,return这种跳转语句。
5.6 方法调用
这一节有一条我之前不知道的信息,但就一句话带过了: 英文中call方法和invoke方法是同义的。 其他的就是说方法调用后程序的流向就先走运行方法再回来之类的。
5.7 返回值
返回的值要和方法要求的返回类型保持一致,没了。
5.8 void类型
虽然void没有返回值,但也可以用return来结束方法的调用。
5.9 参数
简单介绍了一下实参与形参的概念。
5.12 引用类型作为值参数和引用参数
在5.10和5.11的讲解里讲过了。
5.16 方法重载
就相当于写了一个同名但是参数列表不一样的方法。
5.17 命名参数
当你记着一个方法的形参名时,你调用它(如int A(string a,int b,int c))的时候可以通过 A(c:1,a:"牛逼",b:2)这种形式不管形参的位置赋值。
5.18 可选参数
当你定义这样一个方法int A(int a,int b = 3),那么你调用它时可以不用传递b类似 A(1)的方式调用它,而b会有默认值3。 但引用类型的形参只能赋值null作为初始值。而且可选参数必须放在必填参数的后面
5.20 递归
方法自己调用自己,然后把好多个自己压入栈帧然后释放。
二、干货小节
5.3 本地变量
本地变量(也可以理解为局部变量)存在于代码块中,它只活在创建了自己的块里。 这是一件理所当然的事,以至于我特意说出来你可能还会觉得我是别的意思。 那我为什么要说呢? 因为有个叫字段的东西,它也是变量,我认为有必要区别字段和本地变量。复习下,字段是类的成员。
- 本地变量的生命周期在它所创建的块执行结束为止,字段的生命周期从实例创建到不被访问为止
- 本地变量在使用前必须赋值,不管它的类型有无缺省默认值,而字段不用
另外还有一个往期讲过的内容,也是它们的区别:
- 存储位置的区别,字段的类型无论是值类型还是引用类型都在堆里,而对于本地变量,值类型存储在栈里,引用类型的引用存储在栈里,真实数据存储在堆里。
var关键字
可能很多人认为,var是一种动态类型,它是万能的类型。 但实际上,它不是一种类型,我们通常会在不需要指示类型时使用它,比如这种情况:
int a = 1;
A a = new A();
另外,关于C#的var有些要注意的地方:
像这种可以通过右边的赋值判断出左边的类型的情况,就可以使用var。
- 一旦被var修饰并且通过编译,该变量的类型就不可修改了。
- var只能用于本地变量,不能用于字段。
- 只能在变量有初始化的值时可以使用(就是右边的初始赋值)
嵌套块
虽说本地变量的生命周期处于它所在的块中,但这或许说的还不够详细。
嵌套块,就是指块里的块。如:
static void Main(){
int a = 1;
{
int b = 2;
a += b;
System.Console.WriteLine(a);
}
System.Console.Write(a);
//System.Console.Write(b);
}
在嵌套块外面声明的变量a可以在嵌套块内部使用并修改,但解开注释封印想在外面的块里查看b的值会出错。 总结一下,内部块的本地变量生命周期只在内部,程序走到了外部就结束了,外部块的本地变量在内部块正常使用。
5.10 值参数
和值类型有点类似,但这个讲的是参数,而不是类型,你的参数的类型可能是值类型,但这并不代表它是一个值参数。 判断一个参数是不是值参数很简单,不带ref关键字的参数就都是值参数。
它的特点:参数会被复制到栈中。 但是复制到栈中有两种情况,没错,就是参数的类型,它是值类型还是参数类型。 你们关心的肯定是这个,什么情况下,对形参的操作会不会在方法调用结束后影响到原来实参的值。
如果只是想知道这个,我可以先给个结论:
- 引用参数,一定会影响。
- 值参数,参数类型为引用类型,且没有在对形参操作前为形参创建新对象并赋值的情况,会影响。
先讲下作为值参数传递的引用类型参数的情况,之前也说过,引用类型的本地变量它有俩种内存,真正的数据存在堆里,该类型的参数也一样。 虽然值参数会被复制到栈里,但对于引用类型的值参数,复制到栈里的是数据的引用,栈里的复制了的引用和原先实参的引用指向堆里的同一个数据,所以改了形参的值,原先实参的值也会被修改。 那怎么让它不被修改呢,那就是把这个复制的引用,指向另一个地方,比如通过new实例化一个对象赋给形参,这样形参就和原先的实参对象分割开来了。 但要注意,你要是在给形参赋值新对象之前做了操作,该操作也会影响到实参,毕竟赋值之前它们俩指的是同一个对象。
5.11 引用参数
加了ref关键字的参数,如void A(ref int a,ref B b){}; 引用参数相当于实参的一个别名,在方法体内对引用参数的操作就相当于对 传过来的 方法外面的实参 的操作(断句以免难理解)。 另外,这个ref关键字实参和形参都要加上。 并且参数不能是表达式,类似void A(ref 1+1)这种不行。
5.13 输出参数
这也是一种参数。需要在方法定义时的形参前和传递实参时加上out关键字(和引用参数关键字加上的位置没区别,就是ref换成out) 和引用参数一样,相当于实参的别名,但是传递到方法时,参数未被初始化,也就是说,它一开始没有值,而且你必须在方法里使用它之前给它赋值才行,否则会报错。 现在我还不太能理解它的意义,但就是有这么个东西。
5.14 参数数组
需要在方法定义时的形参前面加上param关键字。 比如 int A(param int[] a){}; 好处是什么呢,你传递参数的时候可以这样, A(1,2,3);或者int[] a = {1,2,3};A(a); 可以传递可变数量的同一类型的参数组,也可以直接传递一个数组。
5.15 参数类型总结
5.19 栈帧
之前我们谈过本地变量和参数都会被分配到栈上进行保存,那么以一个方法里的所有相关数据为单位分配到栈上进行保存,这个单位就被称作栈帧。
首先程序运行时,调用Main方法,该方法的栈帧被首先压入栈,中途肯定调用方法,这里称之为A方法,那A方法的栈帧也被压入栈,那A方法就简单处理了一下逻辑也没调用其他方法,那就没有新的方法的栈帧压入栈,A方法完成了它的使命,然后出栈(也被称作栈释放)。
就是这么简单的一个过程。
三、知识迭代
当我发现过去的文章有不合理或者需要补充的地方,我会在此处做一个补充。
1. 关于类型的缺省默认值
某个类型的变量被声明时如果没有被手动赋予初始值,它仍是有值的,这个值是变量所属类型的缺省默认值。 如 int i; 这里i的值是0。 但是这种情况只限于字段(类里的成员变量),不适用于本地变量,本地变量不赋予初始值就被使用会导致编译错误。