方法的由来
由初级程序员到高级程序员进阶,推荐书籍《CLR via C#》、《C# IN DEPTH》
- 方法(method)的前身是C/C++语言的函数(function)
- 方法是面向对象范畴的概念,在非面向对象语言中仍未函数
- 方法永远都是类(或结构体)的成员
- C#语言中函数不可能独立于类(或结构体)之外
- 只有作为类(或结构体)的成员时才被称为方法
- C++中是可以独立于类之外,称为”全局函数”
- 是类(或结构体)最基本的成员之一
- 最基本的成员只有两个:字段与方法,(成员变量与成员函数),本质还是数据
- 方法表示类(或结构体)”能做什么事情”,分实例方法与静态方法
- 为什么需要方法和函数
- 目的1:隐藏复杂的逻辑
- 目的2:复用(reuse,重用)
- 目的3:把大算法分解为小算法
方法的声明与调用
- 申明方法的语法详解
- 参见C#语言文档(申明/定义不分家)
- Parameter全称为”formal parameter”,形式上的参数,简称”形参”
- Parameter是一种变量
- 为方的命名规范
- 大小写规范
- 需要以动词或者动词短语作为名字
- 重温静态(static)方法和实例方法
- 调用方法
- Argument中文C#文档的官方译法为”实际参数”,简称”实参”,可理解为调用方法时的真实条件
调用方法时的argument列表要与定义方法时的parameter列表相匹配
- C#是强类型语言,argument是值、parameter是变量,值与变量一定要匹配,不然编译器会报错
- 示例: ```csharp namespace FunctionDemo { class program { public static void Main(string[] args) { Calculate cal = new Calculate(); int x = cal.Add(1,2);//调用方法 Console.WriteLine(x); } }
class Calculate { //申明方法 public int Add(int a, int b) {
int c = a + b;
return c;
} } } ```
- C#是强类型语言,argument是值、parameter是变量,值与变量一定要匹配,不然编译器会报错
构造器
- 构造器(constructor)是类型的成员之一
- 狭义的构造器指的是”实例构造器”(instance constructor)
如何调用/申明构造器,(VS中的代码片段ctor+tab两次):
- 构造函数用于初始化字段/属性值,每一个实例的成员字段初始化值都一致
- 对象初始化器用于某一个实例的成员字段的初始化
示例如下: ```csharp namespace Constructor { class program { public static void Main(string[] args) {
Student stu = new Student();//调用无参数构造函数
Console.WriteLine(stu.ID);//结果为1
Student stu1 = new Student(3,Dava);//调用有参数构造函数
Console.WriteLine(stu.ID);//结果为3
//对象初始化器
Student stu1 = new Student()
{
ID = 1,
Name = "Bob"
}
} }
class Student { //申明无参数构造器函数 public Student() {
this.ID = 1; this.Name = "No Name";
} //申明带参数构造器函数 public Student(int initId,string initName) {
this.ID = initId; this.Name = initName;
}
public int ID; public string Name; } }
- 构造器的内存原理(以Student stu2 = new Student(3,Jove);为例)
其中:引用类型变量stu2,在栈内存中直接分4个字节大小的内存 ,stu2的内存地址为以10000006为开始地址,以10000009为结束地址,存储的值为实例在堆内存中的地址值200000001(二进制值为00000001 00110001 00101101 00000001)。<br />实例在堆内存中找到空闲内存,地址为20000001,存储实例中的值,ID为uint类型,4个字节,Name为String类型(引用类型),4个字节,一共需要8个字节的空间。<br />由于字段都赋值了,初始不为0,ID为3,Name为Jove,因此ID内存中存储的是3的二进制数,而Name中存储的值为空闲地址30000004的二进制值,在30000004-300000007地址中存储的才是"Jove"的二进制数。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21507654/1623076531062-2c6554de-6583-493f-827c-5c1cdb73b867.png#clientId=u4b1c7f81-1c40-4&from=paste&height=477&id=u18441386&margin=%5Bobject%20Object%5D&name=image.png&originHeight=477&originWidth=1350&originalType=binary&ratio=1&size=75217&status=done&style=none&taskId=u0e581360-c3b8-43f4-b842-4ed63dfe997&width=1350)
<a name="JIqw9"></a>
# 方法的重载(Overload)
- 申明带有重载的方法
- 方法签名(method signature)由方法的名称、类型形参的个数和它的每一个形参(按从左到右的顺序)的类型和种类(值、引用或输出)组成。如下两个方法为重载:
```csharp
public int Add(int a, int b)
{
return a + b;
}
public int Add(int a, int b,int c)
{
return a + b + c;
}
- 方法签名不包含返回类型,例如以下两个函数不属于重载,编译会报错: ```csharp public int Add(int a, int b) { return a + b; }
public double Add(int a, int b) { return a + b; } ```
- 实例构造函数签名由它的每一个形参(按从左到右的顺序)的类型和种类(值、引用或输出)组成。
- 重载决策(到底调用哪一个重载):用于在给定了参数列表和一组候选函数成员的情况下,选择一个最佳函数成员来实施调用。
如何对方法进行debug
- 设置断点(breakpoint)
- 观察方法调用时call stack
- step-in(F11),step-over(F10),step-out(shift+F11)
- 观察局部变量的值与变化
方法调用时栈内存的分配
- stack frame:一个方法在被调用时在栈内存中的布局
- 函数返回值存储在cpu寄存器中
- 方法中即使没有局部变量,也会在栈中有内存,用于存储其他东西
- 方法中调用其他方法(有形参),则此形参变量,由调用者存储在栈中,方法调用结束后,改方法在栈中内存被清空