C#基礎語法 —(4)方法的定義、調用與調試②


一、構造器

  • 構造器(constructor)是類型的成員之一
  • 狹義的構造器指的是“實例構造器”(instance constructor)
  • 如何使用構造器
  • 聲明構造器
  • 構造器的內存原理

構造函數譯爲構造器,成員函數譯爲方法,它們本質都還是函數。

1.構造器的聲明與調用

①默認構造器

namespace ConstructorExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student();//()-調用Student類的構造器
            Console.WriteLine(stu.ID);
        }
    }

    /// <summary>
    ///  聲明一個類,沒有構造器的話;使用默認構造器
    /// </summary>
    class Student
    {
        public int ID;
        public string Name;         
    }
}

  默認構造器將int型的ID初始化爲0:
在這裏插入圖片描述
②無參數構造器

namespace ConstructorExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student();//()-調用Student類的構造器
            Console.WriteLine(stu.ID);
            Console.WriteLine(stu.Name);
        }
    }

 
    class Student
    {
        //自定義構造器Student
        public Student()
        {
            this.ID = 1;
            this.Name = "No Name";
        }

        public int ID;
        public string Name;         
    }
}

在這裏插入圖片描述
③帶參數構造器

namespace ConstructorExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student(2, "小光光");
            Console.WriteLine(stu.ID);
            Console.WriteLine(stu.Name);
        }
    }

 
    class Student
    {
        //自定義帶參數構造器
        public Student(int initID,string initName)
        {
            this.ID = initID;
            this.Name = initName;
        }

        public int ID;
        public string Name;         
    }
}

在這裏插入圖片描述

Code Snippet
Ctrl + Tab * 2:快速生成構造器代碼片段
在這裏插入圖片描述

2.構造器內存原理

①默認構造器圖示

namespace ConstructorExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student();//引用變量-stu;實例-new Student;默認構造器-()         
        }
    }
 
    class Student
    {                               
        public int ID;//int是結構體類型,在內存中佔4個字節
        public string Name;//string是類類型-引用類型,在內存中佔4個字節;存儲的是實例的地址
    }
}

圖中左側指的是棧,右側指的是堆
棧內存分配是從高地址往低地址分配(二進制從下往上,從左往右),直到分配到棧頂。

在這裏插入圖片描述
②帶參數構造器圖示

namespace ConstructorExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student(1,"Mr.Okay");         
        }
    }
 
    class Student
    {
        public Student(int initID,string initName)
        {
            this.ID = initID;
            this.Name = initName;
        }
                                              
        public int ID;//int是結構體類型,在內存中佔4個字節
        public string Name;//string是類類型-引用類型,在內存中佔4個字節;存儲的是實例的地址
    }
}

在這裏插入圖片描述

二、方法的重載(Overload)

在這裏插入圖片描述
WriteLine方法重載示例:
在這裏插入圖片描述
在這裏插入圖片描述

1.聲明帶有重載的方法

①方法簽名中類型形參的個數構成重載:

    class Calculator
    {
        public int Add(int a, int b)
        {
            return a + b;
        }

        public int Add<T>(int a, int b)//類型形參<T>
        {
            T t;//…
            return a + b;
        }
    }

②方法簽名中形參的類型(從左往右)和個數構成重載:

    class Calculator
    {
        public int Add(int a, int b)
        {
            return a + b;
        }

        public int Add(int a, int b, int c)//形參個數不同
        {
            return a + b + c;
        }

        public double Add(int x, double  y)//形參類型不同
        {
            return x + y;
        }
    }

③方法簽名中形參的種類(值、引用或輸出)構成重載:

    class Calculator
    {
        public int Add(int a, int b)
        {
            return a + b;
        }

        public int Add(ref int a, int b)//形參的種類
        {
            return a + b;
        }     
    }

2.重載的調用

namespace ConstructorExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Calculator c = new Calculator();
            int x = c.Add(100, 200);
            int y = c.Add(100,200,300);
            double z = c.Add(100D, 200D);            
        }
    }

    class Calculator
    {
        public int Add(int a, int b)
        {
            return a + b;
        }

        public int Add(int a, int b, int c)
        {
            return a + b + c;
        }

        public double Add(double a, double b)
        {
            return a + b;
        }
    }
}

三、對方法進行debug

  • 設置斷點(breakpoint)
  • 觀察方法調用時的 call stack
  • Step-in、Step-over、Step-out
  • 觀察局部變量的值與變化

    ①調用堆棧 (call stack)
    調試狀態下,菜單 調試 → 窗口 → 調用堆棧
    在這裏插入圖片描述
    ②逐語句(Step-in)
      進入調用的函數內,逐語句進行調試
    ③逐過程(Step-over)
    ④跳出(Step-out)
      用於跳出當前方法並返回到調用它的方法
    ⑤局部變量值的變化
    在這裏插入圖片描述

四、方法的調用與棧

  • 方法調用時棧內存的分配
    • 對 stack frame 的分析

 stack frame:一個方法被調用時,它在棧內存中的佈局。
 C# 中調用方法時的變量歸 Caller(主調函數) 管,不歸 Callee(被調用者) 管。
 壓變量入棧,C# 是從左至右的順序。

 圖示是爲了重點解釋方法、變量、參數的壓棧,實際情況下還要壓入返回地址等。
 返回值一般存在 CPU 的寄存器裏面,特殊情況寄存器存不下該返回值時,會到棧上開闢空間。

 stack overflow 就是棧無限向上延伸(分配變量、參數、棧針等),最後溢出了。
代碼:

namespace ConstructorExample
{
    class Program
    {
        static void Main(string[] args)
        {
            double a = Calculator.GetConeVolume(100, 90);//參數類型是double
        }
    }

    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 時
  Main方法在調用GetConeVolume,需要傳兩個參數,將兩個參數壓入棧中。(壓參數入棧, 是從左至右的順序。)
  因爲 C# 中調用時的參數歸 Caller (主調函數)管,此處即歸 Main 管。

參數r,h — double類型,8個字節
在這裏插入圖片描述

在這裏插入圖片描述
3.進入 GetConeVolume 後
  局部變量是需要入棧的,GetConeVolume 方法中的 局部變量cv 入棧。
  r,h 也是局部變量,但已經作爲參數被 Main 方法壓入棧了,所以它只需要壓 cv 即可。

在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述
4.GetConeVolume 調用 GetCylinderVolume 時
  將兩個參數r,h壓入棧中。
在這裏插入圖片描述
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 也被清空。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章