練習 - 使用 Visual Studio Code 進行偵錯

已完成

是時候開始實踐剛學習到的偵錯工具知識了。 這是您第一天進行此作業,現在是時候可透過修正公司旗艦產品 (一種 Fibonacci 計算機) 中的錯誤 (Bug),讓您的 .NET 偵錯技能發揮作用。

建立範例 .NET 專案以進行偵錯

若要設定 Visual Studio Code 以對 .NET 進行偵錯,首先需要一個 .NET 專案。 Visual Studio Code 包括整合式終端,其能讓建立新專案變得非常簡單。

  1. 在 Visual Studio Code 中,選取 [檔案] > [開啟資料夾]

  2. 在您選擇的位置中建立名為 DotNetDebugging 的新資料夾。 然後選擇 [選取資料夾]

  3. 從 Visual Studio Code 開啟整合式終端機,做法是從主功能表中選取 [檢視]>[終端機]

  4. 在終端視窗中,複製並貼上下列命令:

    dotnet new console
    

    此命令會在您的資料夾中,建立已撰寫基本 "Hello World" 程式的 Program.cs 檔案。 其也會建立名為 DotNetDebugging.csproj 的 C# 專案檔。

  5. 在終端機視窗中,複製並貼上下列命令以執行 "Hello World" 程式。

    dotnet run
    

    終端機視窗會將 "Hello World!" 顯示為輸出。

設定 Visual Studio Code 以對 .NET 進行偵錯

  1. 選取 Program.cs 來加以開啟。

  2. 當您第一次在 Visual Studio Code 中開啟 C# 檔案時,系統將會提示您安裝建議的 C# 延伸模組。 如果您看見此提示,請選取提示中的 [安裝] 按鈕。

    Screenshot of Visual Studio Code prompt to install the C# extension.

  3. Visual Studio Code 將會安裝 C# 延伸模組,而且會顯示額外的提示,以新增必要資產來建置您的專案並對其進行偵錯。 選取 [是] 按鈕。

    Screenshot of Visual Studio Code prompt to add required assets to build and debug your .NET project.

  4. 您可以關閉 [延伸模組: C#] 索引標籤,以專注在我們將進行偵錯的程式碼。

新增 Fibonacci 程式邏輯

我們目前的專案會將 "Hello World" 訊息寫入主控台,這沒有太多可偵錯的內容。 您將改為使用簡短的 .NET 程式,來計算 Fibonacci 數列中的第 N 個數字。

Fibonacci 數列是一組以 0 和 1 開頭的數字,每個後續的數字都是前兩個數字的和。 序列會繼續,如下所示:

0, 1, 1, 2, 3, 5, 8, 13, 21...
  1. 選取 Program.cs 來加以開啟。

  2. 使用下列程式碼取代 Program.cs 的內容:

    int result = Fibonacci(5);
    Console.WriteLine(result);
    
    static int Fibonacci(int n)
    {
        int n1 = 0;
        int n2 = 1;
        int sum;
    
        for (int i = 2; i < n; i++)
        {
            sum = n1 + n2;
            n1 = n2;
            n2 = sum;
        }
    
        return n == 0 ? n1 : n2;
    }
    

    注意

    此程式碼包含錯誤,我們稍後將在本課程模組中對該錯誤進行偵錯。 在我們修正該錯誤 (Bug) 之前,不建議您在任何任務關鍵性 Fibonacci 應用程式中使用此程式碼。

  3. 在 Windows 與 Linux 中,選取 Ctrl+S 以儲存檔案。 在 Mac 中,則選取 Cmd+S

  4. 讓我們先看看更新後的程式碼如何運作,然後再進行偵錯。 在終端中輸入下列命令,以執行程式:

    dotnet run
    

    Terminal window with modified program output.

  5. 終端輸出中會顯示結果 3。 當您查閱此 Fibonacci 序列圖表 (其中顯示括弧中每個值的以零起始的序列位置) 時,您會看到結果應該是 5。 現在是時候來熟悉偵錯工具並修正此程式。

    0 (0), 1 (1), 1 (2), 2 (3), 3 (4), 5 (5), 8 (6), 13 (7), 21 (8)...
    

分析問題

  1. 從左側選取 [執行和偵錯] 索引標籤,然後選取 [開始偵錯] 按鈕,以啟動程式。

    Screenshot of the Start debugging button in Visual Studio Code.

    程式應該很快便會完成。 這是正常的,因為您尚未新增任何中斷點。

  2. 如果未顯示 [偵錯主控台],請在 Windows 與 Linux 中選取 Ctrl+Shift+Y或在 Mac 中選取 Cmd+Shift+Y。 您應該會在結尾處看到數行診斷資訊,後面接著下列行:

    ...
    Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.0\System.Threading.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
    Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.0\System.Text.Encoding.Extensions.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
    3
    The program '[88820] DotNetDebugging.dll' has exited with code 0 (0x0).
    

最上方的數行告知您,預設的偵錯設定會啟用 [Just My Code] 選項。 這表示偵錯工具只會對您的程式碼進行偵錯,且除非您停用此模式,否則將不會逐步執行 .NET 的原始程式碼。 此選項可讓您專注在為程式碼進行偵錯。

在偵錯主控台輸出的結尾,您會看到程式將 "3" 寫入主控台,然後結束並傳回代碼 0。 程式結束代碼 0 通常表示該程式已執行並結束,而且沒有發生任何損毀。 不過,損毀與傳回正確值之間是有差異的。 在此案例中,我們要求程式計算 Fibonacci 數列中的第 5 個值:

0 (0), 1 (1), 1 (2), 2 (3), 3 (4), 5 (5), 8 (6), 13 (7), 21 (8)...

此清單中的第 5 個值為 5,但我們的程式傳回 3。 讓我們使用偵錯工具來診斷並修正此錯誤。

使用中斷點和逐步執行

  1. int result = Fibonacci(5); 上,按一下第 1 行的左邊界以新增中斷點。

    Screenshot of the breakpoint location in the code.

  2. 再次開始偵錯。 程式會開始執行。 其會因為您所設定的中斷點而在第 1 行中斷 (暫停執行)。 使用偵錯工具控制項逐步執行 Fibonacci() 函式。

    Screenshot of the Step into button.

檢查變數狀態

現在花點時間使用 [變數] 面板來檢查不同變數的值。

Screenshot of the Variables panel.

  • 針對 n 參數所顯示的值為何?
  • 在函式開始執行時,區域變數 n1n2sum 的值為何?
  1. 接下來,將使用不進入函式偵錯工具控制項進入 for 迴圈。

    Screenshot of the Step over button.

  2. 繼續向前推進,直到您達到 for 迴圈內部的第一行,即顯示下列內容的那一行:

    sum = n1 + n2;
    

注意

您可能已經注意到,需要在命令中多次逐步執行,才能通過 for(...) {} 行。 此情況之所以會發生,是因為此行上有多個「陳述式」。 當您逐步執行時,您會移至程式碼中的下一個陳述式。 通常每行會有一個陳述式。 如果情況不是這樣,則須多次逐步執行才能移至下一行。

思考一下程式碼

進行偵錯的一個重要部分是停止執行,並對您認為某些程式碼部分 (函式與區塊,例如迴圈) 正嘗試執行的動作做出一些明智的猜測。 如果您不確定也沒關係,那是偵錯程序的過程。 但是,主動參與偵錯程序將協助您更快速地找出錯誤 (Bug)。

在我們進一步深入探索之前,請記住,Fibonacci 數列是一連串以 0 與 1 開頭的數字,每個其他後續的數字都是前兩個數字的總和。

那意味著:

Fibonacci(0) = 0
Fibonacci(1) = 1
Fibonacci(2) = 1 (0 + 1)
Fibonacci(3) = 2 (1 + 1)
Fibonacci(4) = 3 (1 + 2)
Fibonacci(5) = 5 (2 + 3)

了解該定義並查看這個 for 迴圈,我們可以推算:

  1. 迴圈會從 2 算到 n (我們正在尋找的 Fibonacci 序號)。
  2. 如果 n 小於 2,迴圈將永遠不會執行。 如果 n 為 0,函式結尾的 return 陳述式就會傳回 0,如果 n 是 1 或 2,則會傳回 1。 依定義,這些是 Fibonacci 數列中的第 0 個、第 1 個與第 2 個值。
  3. 較有趣的案例是當 n 大於 2 時。 在那些案例中,目前的值定義為前兩個值的總和。 所以,針對此迴圈,n1n2 是前兩個值,而 sum 則是目前反覆項目的值。 因此,每當我們找出前兩個值的總和並將其設定為 sum 時,就會更新 n1n2 值。

好了,我們繼續吧,不需要想過頭。 我們可以多給偵錯工具一點信任。 但值得思考一下程式碼,以查看其是否符合我們的預期,並在其未符合預期時提供更多訊息。

使用中斷點找出錯誤 (Bug)

逐步執行程式碼可能很有助益,但卻很煩人,尤其是當您使用重複呼叫的迴圈或其他程式碼時。 我們可以在迴圈的第一行設定新的中斷點,而不需一再逐步執行迴圈。

當我們這麼做時,請務必策略性地了解放置中斷點的位置。 我們對 sum 的值特別感興趣,因為其代表 Fibonacci 目前的最大值。 因此,讓我們將中斷點放在設定 sum「之後」的那一行。

  1. 在第 13 行新增第二個中斷點。

    Screenshot showing a second breakpoint being set.

    注意

    如果您注意到您正持續執行程式碼,然後逐步執行一行或兩行,則您可以輕鬆地將中斷點更新到更有效率的行。

  2. 既然我們已在迴圈中設定良好的中斷點,現在請使用繼續偵錯工具控制項繼續前進,直到到達中斷點為止。 查看我們的區域變數,即可看到下列幾行:

    n [int]: 5
    n1 [int]: 0
    n2 [int]: 1
    sum [int]: 1
    i [int]: 2
    

    這些行看起來似乎都是正確的。 第一次通過迴圈時,前兩個值的 sum 為 1。 我們可以利用中斷點,直接透過迴圈跳到下一次循環,而不需逐行執行。

  3. 選取 [繼續] 以繼續程式流程,直到到達下一個中斷點為止,這將在下一次通過迴圈時發生。

    注意

    當您使用繼續時,不要太擔心會略過錯誤 (Bug)。 您應該預期您會經常透過程式碼進行偵錯幾次以找出問題所在。 相較於當您逐步執行時過於小心,多次執行通常會比較快。

    這次,我們會看到下列值:

    n [int]: 5
    n1 [int]: 1
    n2 [int]: 1
    sum [int]: 2
    i [int]: 3
    

    讓我們來思考一下。 這些值仍然合理嗎? 似乎合理。 針對第三個 Fibonacci 數字,我們預期會看到 sum 等於 2,而此數字就是 2。

  4. 好的,讓我們選取 [繼續],再次進行迴圈。

    n [int]: 5
    n1 [int]: 1
    n2 [int]: 2
    sum [int]: 3
    i [int]: 4
    

    同樣地,一切看來都不錯。 數列中的第 4 個值應該是 3。

  5. 此時,您可能開始懷疑,程式碼是否一直都是正確的,而且您已經想到該錯誤 (bug)! 讓我們在最後一次執行迴圈時繼續加以使用。 再次選取 [繼續]

    等一下。 程式已完成執行並印出 3! 那並不正確。

    沒關係,別擔心。 我們還沒失敗,我們已了解。 我們現在知道程式碼會正確循環執行迴圈,直到 i 等於 4 為止,但接著,其會在計算最終值之前結束。 我開始對於哪裡會出現錯誤 (Bug) 有一些概念。 您是?

  6. 讓我們在第 17 行設定另一個中斷點,如下:

    return n == 0 ? n1 : n2;
    

    這個中斷點將可讓我們在函式結束之前檢查程式狀態。 我們已經從先前在第 1 行與第 13 行設定的中斷點了解所有可以檢查的內容,因此,可清除這些中斷點。

  7. 移除先前在第 1 行與第 13 行設定的中斷點。 若要這麼做,您可以按一下行號旁之邊界中的中斷點,或在左下方的 [中斷點] 窗格中取消選取第 1 行與第 13 行的中斷點核取方塊。

    Screenshot showing the breakpoints listed in the Breakpoints pane.

    既然我們已經更好地了解運作方式,並設定了中斷點 (旨在攔截行為不當的程式),那麼,我們應該能夠攔截此錯誤 (Bug)!

  8. 最後一次啟動偵錯工具。

    n [int]: 5
    n1 [int]: 2
    n2 [int]: 3
    sum [int]: 3
    

    好吧,那並不正確。 我們特別要求了 Fibonaccci(5),但卻得到 Fibonacci(4)。 此函式會傳回 n2,而每個迴圈反覆項目都會計算 sum 值,並將 n2 設定為等於 sum

    根據此資訊與先前的偵錯回合,我們可以看到當 i 是 4 而不是 5 時,迴圈就會結束。

    讓我們更仔細地查看 for 迴圈的第一行。

    for (int i = 2; i < n; i++)
    

    好的,等一下! 這表示只要一在 for 迴圈的頂端看到 i 不再小於 n,其就會結束。 也就是說,迴圈程式碼不會在 i 等於 n 的情況下執行。 似乎我們想要的是改為一直執行,直到 i <= n

    for (int i = 2; i <= n; i++)
    

    因此,進行該變更之後,您更新的程式看起來應該像這個範例:

    int result = Fibonacci(5);
    Console.WriteLine(result);
    
    static int Fibonacci(int n)
    {
        int n1 = 0;
        int n2 = 1;
        int sum;
    
        for (int i = 2; i <= n; i++)
        {
            sum = n1 + n2;
            n1 = n2;
            n2 = sum;
        }
    
        return n == 0 ? n1 : n2;
    }
    
  9. 停止偵錯工作階段 (如果還未停止)。

  10. 接著,對第 10 行進行前述變更,並將中斷點留在第 17 行。

  11. 重新啟動偵錯工具。 這次,當我們到達第 17 行的中斷點時,將會看到下列值:

    n [int]: 5
    n1 [int]: 3
    n2 [int]: 5
    sum [int]: 5
    

    嘿! 看來我們做到了! 做得很棒,您已為 Fibonacci, Inc. 省下了一天的時間!

  12. 選取 [繼續],以確保程式會傳回正確的值。

    5
    The program '[105260] DotNetDebugging.dll' has exited with code 0 (0x0).
    

    同時還會傳回正確的輸出。

您辦到了! 您已使用 Visual Studio Code 中的 .NET 偵錯工具,針對不是您撰寫的一些程式碼進行偵錯。

在下一個單元中,您將了解如何使用 .NET 內建的記錄和追蹤功能,讓您撰寫的程式碼更容易進行偵錯。