【C#语言入门】07. 方法的定义、调用与调试(下)
三、构造器
- 构造器(constructor)是类型的成员之一
- 狭义的构造器指的是“实例构造器“(instance constructor)
- 如何调用构造器
- 声明构造器
- 构造器的内存原理
1. 实例构造器,声明与调用
internal class Program
{
static void Main(string[] args)
{
Student stu = new Student();
Console.WriteLine(stu.ID);
Console.WriteLine(stu.Name);
Console.WriteLine("======================")
Student stu2 = new Student(2, "Mr.Bu");
Console.WriteLine(stu2.ID);
Console.WriteLine(stu2.Name);
}
}
class Student
{
public Student(int stuid, string stuname)
{
this.ID = stuid;
this.Name = stuname;
}
public Student()
{
this.ID = 0;
this.Name = "No Name";
}
public int ID;
public string Name;
}
一个类如果没有自定义构造器的话他也是有默认的构造器的,而且不显示。自定义的构造器必定是有public且方法名是与类名一致。一个类也可以有多个构造器。(快速写构造器——输入“ctor”——两下Tab键)
2. 构造器的内存原理
new 操作产生实例后,系统会去看你这个实例要占据多少内存空间,如上述例子Student中含有一个int和一个string,32bit+32bit一共占8字节,而构造器的作用就是把划分的8字节内存空间进行具体分割以及初始化。
四、方法的重载(Overload)
- 调用重载方法的示例
- 声明带有重载的方法
- 方法签名(method signature)由方法的名称、类型、形参的个数和它的每一个形参(按从左到右的顺序)的类型和种类(值、引用或者输出)组成。方法签名不包括返回类型。
- 实例构造函数签名由它的每一个形参(按从左到右的顺序)的类型和种类(值、引用、或者输出)组成。
- 重载决策(到底调用哪一个重载):用于在给定了参数列表和一组候选函数成员的情况下,选择一个最佳函数成员来实施调用。
1. 重载的示例
Console.WriteLine("Hello");
Console.WriteLine(100);
Console.WriteLine(200D);
Console.WriteLine(3000L);
都是Console.WriteLine方法,但是他们的形参类型各不相同。
public int Add(int a, int b)
{
return a + b;
}
public int Add(int a, int b, int c)
{
return a + b + c;
}
public int Add(double a, double b)
{
return a + b;
}
都是Add方法,但是他们的形参类型/数量各不相同。
五、如何对方法进行debug
- 设置断点(breakpoint)
- 观察方法调用时的call stack
- Step-in, Step-over, Step-out
- 观察局部变量的值与变化
1. 目的
- 为了找到BUG发生的地方
- 为了动态的了解我们的程序是怎么运行的
2. 设置断点
设置断点后,在调试模式下程序会停在设置断点的那一句代码,供程序员观察。设置断点的方法是在语句行的最左边点一下,会出现一个红圈。
3. Call Stack
会出现被调用的函数以及调用它的函数,甚至是调用调用它的函数,直到找完找到最上层的调用函数。
4. Step-in, Step-over, Step-out
- Step-into:最仔细的debug方法,是会进入到调用的函数中一步一步走。
- Step-over:如果调用了一个方法,它是会直接跳出调用的函数的最终值,在走下去。
- Step-out:直接回到调用当前这个方法的方法中去,相当于在CallStack上调用当前方法的方法上又设了一个断点。
5. 观察局部变量
需要用到locals面板,他会显示用到的本地变量,同时也会标注出变化了的变量。
六、方法的调用与栈
- 方法调用时栈内存的分配(对stack frame的内存分析)
1. stack frame
表示一个方法在被调用时,它在内存空间中的布局。
2. 分配
栈内存是从高字节位到低字节位存储,全满了就会overflow。
class Progarm
{
static void main(string[] args)
{
double result = Calculator.GetConsVolume(100, 100)
}
}
class Calculator
{
public static double GetCircleArea(double r)
{
return Math.PI * r * r;
}
public static double GetCylinderVolume(double r, double h)
{
return GetCircleArea(r) * h;
}
public static double GetConeVolume(double r, double h)
{
return GetCylinderVolume(r, h) /3.0;
}
}
main函数是主调者(Caller),Calculator.GetConsVolume是被调者(Callee),方法的两个变量在C#中是归主调者也就是这里面的main函数管理内存空间的,也就是说谁调用,谁负责将变量从左到右压到栈内存中去。
函数的返回值比较特殊,是存放在CPU的寄存器中的。
一个函数在返回(return)之后,他会释放掉那些没用的内存。比如说主调函数调用的参数。
所以overflow就是因为一直在调用函数但是没有返回,导致栈存爆了。