方法的由来
- 方法(method)的前身是C/C++语言的函数(function)
- 方法是面对对象范畴的概念,在非面对对象的语言中仍然称为函数
- 使用C/C++语言做对比
- 方法永远都是类(或结构体)的成员
- C#语言中函数不可能独立于类(或结构体)之外
- 只有作为类(或结构体)的成员时才被称为方法
- C++中是可以,称为“全局函数”
- 类(或结构体)最基本的成员之一
- 最基本的成员只有两个:字段(成员变量)、方法(成员函数),本质还是数据+算法
- 方法表示类(或结构体)“能做的事情”
为什么需要方法和函数
声明方法的语言详解
- 参见C#与语言文档(声明/定义不分家)
- Parameter全称“formal parameter”形式上的参数,简称“形参”
- Parameter是一种变量
- 为方的命名规范
- 大小写规范
- 需要以动词或则动词宾语作为名字
- 静态方法(static)和实例方法
调用方法
构造器(constructor)是类型的成员之一
- 当你声明一个类的时候没有声明构造器时,类会自动生成一个默认的构造器
- 狭义的构造器值“实例构造器”(instance contructor)
- 如何声明构造器(tab快捷键:ctor)
- 构造器(方法)的名称和类名相同,没有返回值。
- 可以声明多个不同用途的构造器,但是参数列表必须不同
- 如何调用构造器
- 创建不同参数类型和数量的实例时,会自动调用声明后相同参数列表的构造器
- 创建不同参数类型和数量的实例时,会自动调用声明后相同参数列表的构造器
构造器的内存原理
声明带有重载的方法
设置断点
- 设置断点后,当你在调试模式下运行程序时,程序运行到断点处会暂时停止运行,等你观察结束后再继续
- 观察方法调用时的call stack
- call stack的顶层就是当前调用的程序,下一行就是表示从哪一行调用的。当call stack有多层时,显示的有从什么地方调用的这个方法的整个调用链,最底层就是主程序中的某一行。
- 当call static层数特别多时,说明此时调用这个方法的时候会占用大量的栈资源。
- 递归方法就是不断的调用自己,它的栈资源会占用得越来越多,如果在栈资源耗尽之前没有结束递归就会造成栈资源崩溃。

- Step-in,Step-over,Step-out
- Step-in
- 步进调试程序,可以查看一个程序得每一步得过程。
- 在这个期间可以随时看到右下角的call stack一层一层的减少
- Step-over(F10)
- 不进入函数的调用周期,直接到函数结束
- 先用Step-over快速跳过我们知道没有问题的函数调用步骤,到详细的函数调用时使用Step-in进入函数详细查看其过程(配合使用)
- Step-out(Shift + F1)
- 返回到调用它的方法哪一行,也就是返回到call stack里面的第二行的程序
- 返回到调用它的方法哪一行,也就是返回到call stack里面的第二行的程序
- Step-in
观察局部变量的值的变化(Locals)
方法调用时栈内存的分配
- 对stack frame的分析
- stack frame:当一个方法被调用的时候,它在栈内存中的布局。
C# 中调用方法时的变量归 Caller(主调函数) 管,不归 Callee(被调用者) 管。
压变量入栈,C# 是从左至右的顺序。
图示是为了重点解释方法、变量、参数的压栈,实际情况下还要压入返回地址等。
返回值一般存在 CPU 的寄存器里面,特殊情况寄存器存不下该返回值时,会到栈上开辟空间。
stack overflow 就是栈无限向上延伸(分配变量、参数、栈针等),最后溢出了。
代码
using System;namespace CSharpMethodExample{class Program{static void Main(string[] args){double result = Calculator.GetConeVolume(100, 90);}}class Calculator{public static double GetCircleArea(double r){return Math.PI * r * r;}public static double GetCylinderVolume(double r,double h){double a = GetCircleArea(r);return a * h;}public static double GetConeVolume(double r,double h){double cv = GetCylinderVolume(r, h);return cv / 3;}}}
分步讲解
1.进入 Main 方法,调用 GetConeVolume 方法前
在栈上开辟了 Main 方法的 stack frame。
2.Main 方法中调用 GetConeVolume 时
将两个参数压入栈中。因为 C# 中调用时的参数归 Caller(主调者) 管,此处即归 Main 管。从下往上,先压左边的,后压右边的。
3.进入 GetConeVolume 后
局部变量是需要入栈的,GetConeVolume 方法中的 cv 入栈。
r,h 也是局部变量,但已经作为参数被 Main 方法压入栈了,所以它只需要压 cv 即可。
4.GetConeVolume 调用 GetCylinderVolume 时
将两个参数压入栈中。
5.进入 GetCylinderVolume 后
局部变量 a 入栈。
6.GetCylinderVolume 调用 GetCircleArea 时
GetCircleArea 只有一个参数,将其压入栈即可。
7.进入 GetCircleArea 后
GetCircleArea 中没有局部变量,但它在栈上也占内存,它有自己的栈针。
8.GetCircleArea 返回后
返回值存在 CPU 的寄存器(register)里面。
call stack 少了一层。
函数返回后,它所占有的 stack frame 就清空了。
9.GetCylinderVolume 返回后

10.GetConeVolume 返回后
GetConeVolume 的 stack frame被清空。
Main 方法中调用 GetConeVolume 时压入栈中的两个参数也出栈了。
11.Main 返回后(程序结束)
Main 方法的 stack frame 也被清空。
