方法的由来

由初级程序员到高级程序员进阶,推荐书籍《CLR via C#》、《C# IN DEPTH》

  • 方法(method)的前身是C/C++语言的函数(function)
    • 方法是面向对象范畴的概念,在非面向对象语言中仍未函数
  • 方法永远都是类(或结构体)的成员
    • C#语言中函数不可能独立于类(或结构体)之外
    • 只有作为类(或结构体)的成员时才被称为方法
    • C++中是可以独立于类之外,称为”全局函数”
  • 是类(或结构体)最基本的成员之一
    • 最基本的成员只有两个:字段与方法,(成员变量与成员函数),本质还是数据
    • 方法表示类(或结构体)”能做什么事情”,分实例方法与静态方法
  • 为什么需要方法和函数
    • 目的1:隐藏复杂的逻辑
    • 目的2:复用(reuse,重用)
    • 目的3:把大算法分解为小算法

方法的声明与调用

  • 申明方法的语法详解
    • 参见C#语言文档(申明/定义不分家)

image.png

  • 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) {

    1. int c = a + b;
    2. return c;

    } } } ```

构造器

  • 构造器(constructor)是类型的成员之一
  • 狭义的构造器指的是”实例构造器”(instance constructor)
  • 如何调用/申明构造器,(VS中的代码片段ctor+tab两次):

    • 构造函数用于初始化字段/属性值,每一个实例的成员字段初始化值都一致
    • 对象初始化器用于某一个实例的成员字段的初始化
    • 示例如下: ```csharp namespace Constructor { class program { public static void Main(string[] args) {

      1. Student stu = new Student();//调用无参数构造函数
      2. Console.WriteLine(stu.ID);//结果为1
      3. Student stu1 = new Student(3Dava);//调用有参数构造函数
      4. Console.WriteLine(stu.ID);//结果为3
      5. //对象初始化器
      6. Student stu1 = new Student()
      7. {
      8. ID = 1,
      9. Name = "Bob"
      10. }

      } }

      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寄存器中
  • 方法中即使没有局部变量,也会在栈中有内存,用于存储其他东西
  • 方法中调用其他方法(有形参),则此形参变量,由调用者存储在栈中,方法调用结束后,改方法在栈中内存被清空