練習 - 使用 Visual Studio Code 進行偵錯
是時候開始實踐剛學習到的偵錯工具知識了。 這是您第一天進行此作業,現在是時候可透過修正公司旗艦產品 (一種 Fibonacci 計算機) 中的錯誤 (Bug),讓您的 .NET 偵錯技能發揮作用。
建立範例 .NET 專案以進行偵錯
若要設定 Visual Studio Code 以對 .NET 進行偵錯,首先需要一個 .NET 專案。 Visual Studio Code 包括整合式終端,其能讓建立新專案變得非常簡單。
在 Visual Studio Code 中,選取 [檔案] > [開啟資料夾]。
在您選擇的位置中建立名為
DotNetDebugging
的新資料夾。 然後選擇 [選取資料夾]。從 Visual Studio Code 開啟整合式終端機,做法是從主功能表中選取 [檢視]>[終端機]。
在終端視窗中,複製並貼上下列命令:
dotnet new console
此命令會在您的資料夾中,建立已撰寫基本 "Hello World" 程式的 Program.cs 檔案。 其也會建立名為 DotNetDebugging.csproj 的 C# 專案檔。
在終端機視窗中,複製並貼上下列命令以執行 "Hello World" 程式。
dotnet run
終端機視窗會將 "Hello World!" 顯示為輸出。
設定 Visual Studio Code 以對 .NET 進行偵錯
選取 Program.cs 來加以開啟。
當您第一次在 Visual Studio Code 中開啟 C# 檔案時,系統將會提示您安裝建議的 C# 延伸模組。 如果您看見此提示,請選取提示中的 [安裝] 按鈕。
Visual Studio Code 將會安裝 C# 延伸模組,而且會顯示額外的提示,以新增必要資產來建置您的專案並對其進行偵錯。 選取 [是] 按鈕。
您可以關閉 [延伸模組: C#] 索引標籤,以專注在我們將進行偵錯的程式碼。
新增 Fibonacci 程式邏輯
我們目前的專案會將 "Hello World" 訊息寫入主控台,這沒有太多可偵錯的內容。 您將改為使用簡短的 .NET 程式,來計算 Fibonacci 數列中的第 N 個數字。
Fibonacci 數列是一組以 0 和 1 開頭的數字,每個後續的數字都是前兩個數字的和。 序列會繼續,如下所示:
0, 1, 1, 2, 3, 5, 8, 13, 21...
選取 Program.cs 來加以開啟。
使用下列程式碼取代 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 應用程式中使用此程式碼。
在 Windows 與 Linux 中,選取 Ctrl+S 以儲存檔案。 在 Mac 中,則選取 Cmd+S。
讓我們先看看更新後的程式碼如何運作,然後再進行偵錯。 在終端中輸入下列命令,以執行程式:
dotnet run
終端輸出中會顯示結果 3。 當您查閱此 Fibonacci 序列圖表 (其中顯示括弧中每個值的以零起始的序列位置) 時,您會看到結果應該是 5。 現在是時候來熟悉偵錯工具並修正此程式。
0 (0), 1 (1), 1 (2), 2 (3), 3 (4), 5 (5), 8 (6), 13 (7), 21 (8)...
分析問題
從左側選取 [執行和偵錯] 索引標籤,然後選取 [開始偵錯] 按鈕,以啟動程式。 您可能需要先選取 [執行並偵錯] 按鈕,然後選取 Program.cs 檔案。
程式應該很快便會完成。 這是正常的,因為您尚未新增任何中斷點。
如果未顯示 [偵錯主控台],請在 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。 讓我們使用偵錯工具來診斷並修正此錯誤。
使用中斷點和逐步執行
在
int result = Fibonacci(5);
上,按一下第 1 行的左邊界以新增中斷點。再次開始偵錯。 程式會開始執行。 其會因為您所設定的中斷點而在第 1 行中斷 (暫停執行)。 使用偵錯工具控制項逐步執行
Fibonacci()
函式。
檢查變數狀態
現在花點時間使用 [變數] 面板來檢查不同變數的值。
- 針對
n
參數所顯示的值為何? - 在函式開始執行時,區域變數
n1
、n2
和sum
的值為何?
接下來,將使用不進入函式偵錯工具控制項進入
for
迴圈。繼續向前推進,直到您達到
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
迴圈,我們可以推算:
- 迴圈會從 2 算到
n
(我們正在尋找的 Fibonacci 序號)。 - 如果
n
小於 2,迴圈將永遠不會執行。 如果n
為 0,函式結尾的return
陳述式就會傳回 0,如果n
是 1 或 2,則會傳回 1。 依定義,這些是 Fibonacci 數列中的第 0 個、第 1 個與第 2 個值。 - 較有趣的案例是當
n
大於 2 時。 在那些案例中,目前的值定義為前兩個值的總和。 所以,針對此迴圈,n1
與n2
是前兩個值,而sum
則是目前反覆項目的值。 因此,每當我們找出前兩個值的總和並將其設定為sum
時,就會更新n1
與n2
值。
好了,我們繼續吧,不需要想過頭。 我們可以多給偵錯工具一點信任。 但值得思考一下程式碼,以查看其是否符合我們的預期,並在其未符合預期時提供更多訊息。
使用中斷點找出錯誤 (Bug)
逐步執行程式碼可能很有助益,但卻很煩人,尤其是當您使用重複呼叫的迴圈或其他程式碼時。 我們可以在迴圈的第一行設定新的中斷點,而不需一再逐步執行迴圈。
當我們這麼做時,請務必策略性地了解放置中斷點的位置。 我們對 sum
的值特別感興趣,因為其代表 Fibonacci 目前的最大值。 因此,讓我們將中斷點放在設定 sum
「之後」的那一行。
在第 13 行新增第二個中斷點。
注意
如果您注意到您正持續執行程式碼,然後逐步執行一行或兩行,則您可以輕鬆地將中斷點更新到更有效率的行。
既然我們已在迴圈中設定良好的中斷點,現在請使用繼續偵錯工具控制項繼續前進,直到到達中斷點為止。 查看我們的區域變數,即可看到下列幾行:
n [int]: 5 n1 [int]: 0 n2 [int]: 1 sum [int]: 1 i [int]: 2
這些行看起來似乎都是正確的。 第一次通過迴圈時,前兩個值的
sum
為 1。 我們可以利用中斷點,直接透過迴圈跳到下一次循環,而不需逐行執行。選取 [繼續] 以繼續程式流程,直到到達下一個中斷點為止,這將在下一次通過迴圈時發生。
注意
當您使用繼續時,不要太擔心會略過錯誤 (Bug)。 您應該預期您會經常透過程式碼進行偵錯幾次以找出問題所在。 相較於當您逐步執行時過於小心,多次執行通常會比較快。
這次,我們會看到下列值:
n [int]: 5 n1 [int]: 1 n2 [int]: 1 sum [int]: 2 i [int]: 3
讓我們來思考一下。 這些值仍然合理嗎? 似乎合理。 針對第三個 Fibonacci 數字,我們預期會看到
sum
等於 2,而此數字就是 2。好的,讓我們選取 [繼續],再次進行迴圈。
n [int]: 5 n1 [int]: 1 n2 [int]: 2 sum [int]: 3 i [int]: 4
同樣地,一切看來都不錯。 數列中的第 4 個值應該是 3。
此時,您可能開始懷疑,程式碼是否一直都是正確的,而且您已經想到該錯誤 (bug)! 讓我們在最後一次執行迴圈時繼續加以使用。 再次選取 [繼續]。
等一下。 程式已完成執行並印出 3! 那並不正確。
沒關係,別擔心。 我們還沒失敗,我們已了解。 我們現在知道程式碼會正確循環執行迴圈,直到
i
等於 4 為止,但接著,其會在計算最終值之前結束。 我開始對於哪裡會出現錯誤 (Bug) 有一些概念。 您是?讓我們在第 17 行設定另一個中斷點,如下:
return n == 0 ? n1 : n2;
這個中斷點將可讓我們在函式結束之前檢查程式狀態。 我們已經從先前在第 1 行與第 13 行設定的中斷點了解所有可以檢查的內容,因此,可清除這些中斷點。
移除先前在第 1 行與第 13 行設定的中斷點。 若要這麼做,您可以按一下行號旁之邊界中的中斷點,或在左下方的 [中斷點] 窗格中取消選取第 1 行與第 13 行的中斷點核取方塊。
既然我們已經更好地了解運作方式,並設定了中斷點 (旨在攔截行為不當的程式),那麼,我們應該能夠攔截此錯誤 (Bug)!
最後一次啟動偵錯工具。
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; }
停止偵錯工作階段 (如果還未停止)。
接著,對第 10 行進行前述變更,並將中斷點留在第 17 行。
重新啟動偵錯工具。 這次,當我們到達第 17 行的中斷點時,將會看到下列值:
n [int]: 5 n1 [int]: 3 n2 [int]: 5 sum [int]: 5
嘿! 看來我們做到了! 做得很棒,您已為 Fibonacci, Inc. 省下了一天的時間!
選取 [繼續],以確保程式會傳回正確的值。
5 The program '[105260] DotNetDebugging.dll' has exited with code 0 (0x0).
同時還會傳回正確的輸出。
您辦到了! 您已使用 Visual Studio Code 中的 .NET 偵錯工具,針對不是您撰寫的一些程式碼進行偵錯。
在下一個單元中,您將了解如何使用 .NET 內建的記錄和追蹤功能,讓您撰寫的程式碼更容易進行偵錯。