一、实例字段和局部变量
实例字段 | 局部变量 | |
---|---|---|
生存期 | 从实例被创建时开始,直到实例不再被访问时结束 | 从它在块中被声明的那一刻开始,在块完成执行时结束 |
隐式初始化 | 初始化成该类型的默认值 | 没有隐式初始化。如果变量在使用之前没有被赋值,编译器就会产生一条错误消息 |
存储区域 | 由于实例字段是类的成员,所以所有字段都存储在堆里,无论它们是值类型的还是引用类型的 | 值类型:存储在栈里 引用类型:引用存储在栈里,数据存储在堆里 |
1、使用 var 关键字条件:
- 只用于局部变量,不能用于字段;
- 只能在变量声明中包含初始化时使用;
- 一旦编译器推断变量的类型,它就是固定且不能更改的。
2、局部常量
常量的两个特征:
- 在声明时必须初始化;
- 在声明后不能改变;
const Type Indentifier = Value;
声明特点:
- 类型前添加 const
- 必须初始化语句。初始化值必须在编译期决定。可以是 null 引用,但不能是某对象的引用。(对象的引用是在运行时决定的)
二、控制流
表 - 控制流描述
类型 | 关键字 | 描述 |
---|---|---|
选择语句 | if | 有条件地执行一条语句。 |
选择语句 | if…else | 有条件地执行一条或另一条语句。 |
选择语句 | switch | 有条件地执行一组语句中的某一条。 |
迭代语句 | for 循环 | 在顶部测试 |
迭代语句 | while 循环 | 在顶部测试 |
迭代语句 | do 循环 | 在底部测试 |
迭代语句 | foreach 循环 | 为一组中每个成员执行一次 |
跳转语句 | break | 跳出当前循环 |
跳转语句 | continue | 到当前循环的底部 |
跳转语句 | goto | 到一个命名的语句 |
跳转语句 | return | 返回到调用方法继续执行 |
三、形参和实参
形参: 声明在方法的参数列表中
实参: 用于初始化形参的表达式或变量
自动初始化
变量类型 | 存储位置 | 自动初始化 | 用途 |
---|---|---|---|
局部变量 | 栈或者栈和堆 | ❌ | 用于函数成员内部的局部计算 |
类字段 | 堆 | ✔ | 类的成员 |
结构字段 | 栈或堆 | ✔ | 结构的成员 |
参数 | 栈 | ❌ | 用于把值传入或传出方法 |
数组元素 | 堆 | ✔ | 数组的成员 |
1、dynamic关键字
在编译时,编译器不会对 dynamic 类型的变量做类型检查。相反,它将与该变量及该变量的操作有关的所有信息打包。在运行时,会对这些信息进行检查,以确保它与变量所代表的实际类型一致。否则,将在运行时抛出异常。
2、值参数
参数以不同的方式从方法传入或传出数据,称为值参数。
使用值参数时,通过将实参的值复制到形参的方式把数据传递给方法。方法被调用时,系统执行如下操作。
- 在栈中为形参分配空间。
- 将实参的值复制给形参。
3、引用参数
引用参数
- 使用引用参数时,必须在方法的声明和调用中都使用 ref 修饰符。
- 实参必须是变量,在用作实参前必须被赋值。如果是引用类型变量,可以赋值为一个引用或 null。
引用参数具有以下特征: - 不会在栈上为形参分配内存。
- 形参的参数名将作为实参变量的别名,指向相同的内存位置。
4、引用类型作为值参数和引用参数
在方法内设置引用类型形参时:
- 将引用类型对象作为值参数传递
如果在方法内创建一个新对象并赋值给形参,将切断形参与实参之间的关联,并且在方法调用结束后,新对象也将不复存在。
(注意:在方法内,对象所引用的数据成员会有改变) - 将引用类型对象作为引用参数传递
如果在方法内创建一个新对象并赋值给形参,在方法结束后该对象依然存在,并且是实参所引用的值。
四、参数数组
特点:
- 在一个参数列表中只能有一个参数数组;
- 如果有,它必须是列表中的最后一个;
由于参数数组表示的所有参数必须是同一类型
声明参数数组时:
- 在数据类型前使用 params 修饰符。
- 在数据类型后放置一组空的方括号;
void ListInts(params int[] inVals)
{
}
调用者
void Fun()
{
int[] intArray = {1,2,3};
ListInts(intArray);
//或者
ListInts(10,20,30);
}
参数类型总结
参数类型 | 修饰符 | 是否在声明时使用 | 是否在调用时使用 | 执行 |
---|---|---|---|---|
值 | 无 | 系统把实参的值赋值到形参 | ||
引用 | ref | ✔ | ✔ | 形参是实参的别名 |
输出 | out | ✔ | ✔ | 仅包含一个返回的值。形参是实参的别名 |
数组 | params | ✔ | ❌ | 允许传递可变数目的实参方法 |
五、ref 局部变量 和 ref 返回
//出自《C#图解教程》 -第六章方法-6.17的例子,本书能够输出结果。
//可同样的代码,在自己的编译器里运行会报错,提示“类结构或接口成员声明中的标记无效”
//这个语法,真的可以声明一个 return ref 的函数吗?(很不理解!!!)
class Simple
{
private int Score = 5;
public ref int RefToValue()
{
return ref Score;
}
}
后面的例子我也没法继续学习了。同样的代码,在编译器里也是报错的。并且,在网上没有查到更多相关的资料。(微软开发文档也没有找到)不过也看到有些网友实现 了rerurn ref 的例子。这反而让我更困惑了!
六、栈帧
在调用方法的时候,内存从栈的顶部开始分配,保存和方法关联的一些数据项,这块内存叫作方法的栈帧(stack frame)。
1、栈帧包含的内存保存:
- 返回地址,也就是在方法退出的时候继续执行的位置。
- 分配内存的参数,也就是方法的值参数,还可能是参数数组(如果有的话)。(本书应该写漏了,我认为分配内存还包括局部变量)
- 和方法调用相关的其他管理数据项。(不太懂?数据项包括局部变量吗,还是其他的数据项)
2、在方法调用和时,整个栈帧都会压入栈。
3、在方法退出的时候,整个栈帧都会从栈上弹出。弹出栈帧有的时候也叫作栈展开(unwind)。