毫無疑問,我們作為軟體開發人員編寫的程式碼並不總是按照我們的預期進行。 有時它會做一些完全不同的事情! 當意外發生時,下一個任務是找出原因,儘管我們可能會想花幾個小時盯著我們的程式碼,但使用偵錯工具或偵錯器會更容易、更有效率。
不幸的是,偵錯器並不是可以神奇地揭示我們程式碼中的所有問題或「錯誤」的東西。 偵錯 是指在 Visual Studio 等偵錯工具中逐步執行程式碼,以找出您犯程式設計錯誤的確切點。 然後,您了解需要在代碼中進行哪些更正,而調試工具通常允許您進行臨時更改,以便您可以繼續運行程序。
有效使用調試器也是一項需要時間和練習來學習的技能,但最終是每個軟體開發人員的基本任務。 在本文中,我們介紹了調試的核心原理,並提供了幫助您入門的技巧。
通過問自己正確的問題來澄清問題
它有助於在嘗試修復之前澄清您遇到的問題。 我們預計您已經在程式碼中遇到了問題,否則您不會在這裡嘗試弄清楚如何調試它! 因此,在開始調試之前,請確保您已確定要解決的問題:
您期望您的程式碼做什麼?
相反發生了什麼?
如果您在執行應用程式時遇到錯誤(異常),這可能是一件好事! 例外狀況是執行程式碼時遇到的意外事件,通常是某種類型的錯誤。 偵錯工具可以將您帶到程式碼中發生例外狀況的確切位置,並可以幫助您調查可能的修正。
如果發生了其他事情,問題的症狀是什麼? 您是否已經懷疑此問題在您的程式碼中發生在哪裡? 例如,如果您的程式碼顯示一些文字,但文字不正確,您就知道您的資料不正確,或者設定顯示文字的程式碼有某種錯誤。 透過逐步執行偵錯工具中的程式碼,您可以檢查變數的每一個變更,以確切發現指派不正確值的時間和方式。
檢查你的假設
在調查漏洞或錯誤之前,請先思考讓您預期特定結果的那些假設。 在偵錯工具中,即使您正在直接查看問題原因,隱藏或未知的假設仍可能妨礙您識別問題。 您可能有一長串可能的假設! 這裡有幾個問題可以問自己,以挑戰你的假設。
您是否使用正確的 API (即正確的物件、函式、方法或屬性)? 您使用的 API 可能不會執行您認為的動作。 (在偵錯工具中檢查 API 呼叫之後,修正它可能需要查閱文件,以協助識別正確的 API。)
您是否正確使用 API? 也許您使用了正確的 API,但沒有以正確的方式使用它。
您的程式碼是否包含任何拼字錯誤? 有些拼字錯誤,例如變數名稱的簡單拼字錯誤,可能很難看到,尤其是在使用不需要在使用變數之前聲明變數的語言時。
您是否對程式碼進行了更改,並假設它與您看到的問題無關?
您是否期望物件或變數包含與實際發生的情況不同的特定值(或某種類型的值)?
你知道程式碼的意圖嗎? 調試其他人的代碼通常更困難。 如果這不是您的程式碼,您可能需要花時間準確了解程式碼的作用,然後才能有效地調試它。
小提示
編寫程式碼時,請從小事做起,從有效的程式碼開始! (好的範例程式碼在這裡很有幫助。有時,從一小段程式碼開始,示範您要實現的核心任務,更容易修復一組大型或複雜的程式碼。 然後,您可以增量修改或新增程式碼,並在每個點測試錯誤。
透過質疑您的假設,您可以減少在程式碼中發現問題所需的時間。 您也可以減少修正問題所需的時間。
在偵錯模式中逐步執行程式碼,以找出問題發生的位置
當您通常執行應用程式時,只有在程式碼執行之後,您才會看到錯誤和不正確的結果。 程式也可能意外終止,但不會告訴您原因。
當您在偵錯工具中執行應用程式時,也稱為 偵錯模式,偵錯工具會主動監視程式執行時發生的所有事情。 它還允許您隨時暫停應用程序以檢查其狀態,然後逐行逐步瀏覽代碼以觀察發生的每個細節。
在 Visual Studio 中,您可以使用 F5 (或 [ 偵錯>開始偵錯 ] 功能表命令或 [偵錯] 工具列中的 [ 開始偵錯 ]
圖示) 進入偵錯模式。 如果發生任何例外狀況,Visual Studio 的例外狀況協助程式會將您帶到發生例外狀況的確切位置,並提供其他有用的資訊。 如需如何在程式代碼中處理例外狀況的詳細資訊,請參閱 偵錯技術和工具。
如果您沒有收到例外狀況,您可能很清楚在程式碼中尋找問題的位置。 此步驟是您將 中斷點 與偵錯工具搭配使用的地方,讓自己有機會更仔細地檢查程式碼。 斷點是可靠的除錯功能中最基本且必不可少的特性。 中斷點會指出 Visual Studio 應該暫停執行中程式碼的位置,以便您可以查看變數的值,或記憶體的行為,以及程式碼執行的順序。
在 Visual Studio 中,您可以按一下程式碼行旁的左邊界,以快速設定中斷點。 或者將游標放在一條線上,然後按 F9。
為了協助說明這些概念,我們會引導您完成一些已經有數個錯誤的範例程式碼。 我們使用的是 C#,但偵錯功能適用於 Visual Basic、C++、JavaScript、Python 和其他支援的語言。 也提供了 Visual Basic 的範例程式碼,但螢幕擷取畫面是 C# 的。
建立範例應用程式 (有一些錯誤)
接下來,您建立一個具有一些錯誤的應用程式。
您必須安裝 Visual Studio,並安裝 .NET 桌面開發 工作負載。
如果您尚未安裝 Visual Studio,請移至 Visual Studio 下載 頁面免費安裝。
如果您需要安裝工作負載,但已經有 Visual Studio,請選取 [工具>] [取得工具和功能]。 Visual Studio 安裝程式隨即啟動。 選擇 [.NET 桌面開發] 工作負載,然後選擇 [修改]。
開啟 Visual Studio。
在開始視窗中,選擇 Create a new project (建立新專案)。 在搜尋方塊中輸入 console ,選取 C# 或 Visual Basic 作為語言,然後選擇 [適用於 .NET 的 主控台應用程式 ]。 選擇下一步。 輸入 ConsoleApp_FirstApp 作為專案名稱,然後選取 [下一步]。
如果您使用不同的專案名稱,則在複製範例程式碼時,將需要修改命名空間值以符合您的專案名稱。
選擇建議的目標架構或 .NET 8,然後選擇 建立。
如果您未看到適用於 .NET 的 [主控台應用程式] 專案範本,請前往 [工具]>[取得工具與功能...],以開啟 Visual Studio 安裝程式。 選擇 [.NET 桌面開發] 工作負載,然後選擇 [修改]。
Visual Studio 隨即建立主控台專案,並出現在右窗格的 [方案總管] 中。
在 Program.cs (或 Program.vb) 中,將所有預設程式碼取代為下列程式碼。 (先選取正確的語言索引標籤,C# 或 Visual Basic。
using System; using System.Collections.Generic; namespace ConsoleApp_FirstApp { class Program { static void Main(string[] args) { Console.WriteLine("Welcome to Galaxy News!"); IterateThroughList(); Console.ReadKey(); } private static void IterateThroughList() { var theGalaxies = new List<Galaxy> { new Galaxy() { Name="Tadpole", MegaLightYears=400, GalaxyType=new GType('S')}, new Galaxy() { Name="Pinwheel", MegaLightYears=25, GalaxyType=new GType('S')}, new Galaxy() { Name="Cartwheel", MegaLightYears=500, GalaxyType=new GType('L')}, new Galaxy() { Name="Small Magellanic Cloud", MegaLightYears=.2, GalaxyType=new GType('I')}, new Galaxy() { Name="Andromeda", MegaLightYears=3, GalaxyType=new GType('S')}, new Galaxy() { Name="Maffei 1", MegaLightYears=11, GalaxyType=new GType('E')} }; foreach (Galaxy theGalaxy in theGalaxies) { Console.WriteLine(theGalaxy.Name + " " + theGalaxy.MegaLightYears + ", " + theGalaxy.GalaxyType); } // Expected Output: // Tadpole 400, Spiral // Pinwheel 25, Spiral // Cartwheel, 500, Lenticular // Small Magellanic Cloud .2, Irregular // Andromeda 3, Spiral // Maffei 1, 11, Elliptical } } public class Galaxy { public string Name { get; set; } public double MegaLightYears { get; set; } public object GalaxyType { get; set; } } public class GType { public GType(char type) { switch(type) { case 'S': MyGType = Type.Spiral; break; case 'E': MyGType = Type.Elliptical; break; case 'l': MyGType = Type.Irregular; break; case 'L': MyGType = Type.Lenticular; break; default: break; } } public object MyGType { get; set; } private enum Type { Spiral, Elliptical, Irregular, Lenticular} } }我們使用此程式碼的目的是在清單中顯示星系名稱、到星系的距離以及星系類型。 若要偵錯,請務必瞭解程式碼的意圖。 以下是我們要在輸出中顯示的清單中一行的格式:
星系名稱、 距離、 星系類型。
執行應用程式
按 F5 或位於程式碼編輯器上方的偵錯工具列中 [ 開始偵錯 ] 按鈕 ![顯示 [開始偵錯] 按鈕圖示。](media/dbg-tour-start-debugging.png?view=visualstudio)
應用程式會啟動,而且偵錯工具不會向我們顯示任何例外狀況。 不過,您在主控台視窗中看到的輸出並非您預期的輸出。 以下是預期的輸出:
Tadpole 400, Spiral
Pinwheel 25, Spiral
Cartwheel, 500, Lenticular
Small Magellanic Cloud .2, Irregular
Andromeda 3, Spiral
Maffei 1, Elliptical
但是,你會看到以下輸出:
Tadpole 400, ConsoleApp_FirstApp.GType
Pinwheel 25, ConsoleApp_FirstApp.GType
Cartwheel, 500, ConsoleApp_FirstApp.GType
Small Magellanic Cloud .2, ConsoleApp_FirstApp.GType
Andromeda 3, ConsoleApp_FirstApp.GType
Maffei 1, 11, ConsoleApp_FirstApp.GType
查看輸出和我們的代碼,我們知道 GType 是用來存儲星系類型的類別名稱。 我們試圖顯示實際的星系類型(例如“螺旋”),而不是類別名稱!
對應用程式進行偵錯
在應用程式仍在執行的情況下,插入中斷點。
在迴圈中
foreach,在方法旁邊點擊右鍵Console.WriteLine,以開啟內容選單,然後從彈出式選單中選擇中斷點>插入中斷點。foreach (Galaxy theGalaxy in theGalaxies) { Console.WriteLine(theGalaxy.Name + " " + theGalaxy.MegaLightYears + ", " + theGalaxy.GalaxyType); }當您設定中斷點時,左邊界會出現一個紅點。
當您在輸出中看到問題時,您會在偵錯工具中查看設定輸出之前的程式碼,以開始調試。
選擇偵錯工具列中的 重新啟動
(Ctrl + Shift + F5)。應用程式會在您設定的中斷點暫停。 黃色醒目提示指出偵錯工具暫停的位置 (黃色程式碼行尚未執行) 。
將游標停留在右側的變數上
GalaxyType,然後在扳手圖示的左側展開theGalaxy.GalaxyType。 您會看到GalaxyType包含屬性MyGType,且屬性值設定為Spiral。「Spiral」確實是您期望列印到控制台的正確值! 因此,您可以在執行應用程式時存取此程式碼中的值,這是一個良好的開始。 在此案例中,我們使用的 API 不正確。 讓我們看看您是否可以在偵錯工具中執行程式碼時修正此問題。
在同一程式碼中,在仍在偵錯的同時,將游標放在 的
theGalaxy.GalaxyType末尾,並將其變更為theGalaxy.GalaxyType.MyGType。 雖然您可以進行編輯,但程式碼編輯器會顯示錯誤 (紅色波浪線) 。 (在 Visual Basic 中,不會顯示錯誤,而且這部分程式碼可以運作。按 F11 (偵錯>單步執入 或偵錯工具列中的 單步執入 按鈕)來執行當前代碼行。
F11 會讓除錯器前進並逐一執行程式碼的陳述。 F10 (步過)是類似的命令,兩者在學習如何使用除錯器時都很有用。
當您嘗試推進偵錯工具時,會出現 [熱重新載入] 對話方塊,指出無法編譯編輯。
[編輯並繼續] 對話方塊隨即出現,表示無法編譯編輯內容。
備註
若要偵錯 Visual Basic 範例程式碼,請略過接下來的幾個步驟,直到系統指示您按一下 [偵錯] 工具列中的 [重新啟動] 按鈕
,該按鈕在 [重新啟動應用程式] 的圖示 。中顯示 選取編輯在熱重新載入或編輯並繼續訊息方塊中。 您現在會在 [錯誤清單] 視窗中看到錯誤訊息。 此錯誤表示
'object'中不包含MyGType的定義。即使我們使用類型的
GType物件設定每個星系(具有屬性MyGType),偵錯工具也無法將物件theGalaxy識別為類型的GType物件。 這是怎麼回事呢? 您想要查看任何設定星系類型的程式碼。 當你這樣做時,你會看到類別GType確實有一個MyGType屬性,但似乎有些地方出了問題。 關於object的錯誤訊息其實是線索,對於語言解釋器來說,類型似乎是object類型物件,而不是GType類型物件。查看與設定星系類型相關的程式碼時,您會發現
GalaxyType類別的Galaxy屬性被指定為object,而不是GType。public object GalaxyType { get; set; }變更上述程式碼,如下所示:
public GType GalaxyType { get; set; }選取 [偵錯工具列] 中的 重新啟動 按鈕圖示(Ctrl + Shift + F5)以重新編譯程式碼並重新啟動應用程式。
現在,當偵錯工具暫停在
Console.WriteLine處時,您可以將滑鼠懸停在theGalaxy.GalaxyType.MyGType上,並查看值是否已正確設定。按一下左邊界中的中斷點圓圈 (或按一下滑鼠右鍵並選擇 [中斷點>刪除中斷點]) 來移除中斷點,然後按 F5 繼續。
該應用程序運行並顯示輸出。 它看起來不錯,但你注意到一件事。 您預計小麥哲倫星雲星系會在控制台輸出中顯示為不規則星系,但它根本沒有顯示星系類型。
Tadpole 400, Spiral Pinwheel 25, Spiral Cartwheel, 500, Lenticular Small Magellanic Cloud .2, Andromeda 3, Spiral Maffei 1, Elliptical在
switch陳述式之前(如果是 Visual Basic,則在Select陳述式之前),設置這行程式碼的中斷點。public GType(char type)這段程式碼是設定星系類型的地方,所以我們想仔細看看它。
選取
的 [重新啟動圖示]。偵錯工具列中的按鈕 (Ctrl + Shift + F5) 以重新啟動。偵錯工具會在您設定中斷點的程式碼行上暫停。
將滑鼠停留在變數上
type。 您會看到值S(在字元代碼之後)。 您對 的值I感興趣,因為您知道這是一種不規則星系類型。按 F5 並再次將游標停留在變數上
type。 重複此步驟,直到您在type變數中看到I的值為止。
現在,按 F11 (調試>單步執行)。
按 F11 直到您停止在具有 'I' 值的程式碼行,該行位於
switch陳述式中(SelectVisual Basic 的陳述式)。 在這裡,您會看到由拼寫錯誤引起的明顯問題。 您預期程式碼會前進到將MyGType設定為不規則星系類型的位置,但偵錯工具反而會完全跳過這段程式碼,並在switch陳述式中的default區段 (Else在 Visual Basic 中) 暫停。
查看程式碼,您會看到陳述式中有
case 'l'拼字錯誤。 應該是。case 'I'選取程式碼中的
case 'l',並將其替換成case 'I'。移除中斷點,然後選取 [ 重新啟動 ] 按鈕以重新啟動應用程式。
錯誤現在已修復,您會看到您期望的輸出!
按任意鍵完成應用程序。
總結
當您看到問題時,請使用偵錯工具和 步驟命令 ( 例如 F10 和 F11 ) 來尋找有問題的程式碼區域。
備註
如果難以識別發生問題的程式碼區域,請在問題發生之前執行的程式碼中設定中斷點,然後使用步驟命令,直到您看到問題資訊清單為止。 您也可以使用 追蹤點 ,將訊息記載至 「輸出」 視窗。 透過查看記錄的訊息 (並注意哪些訊息尚未記錄!),您通常可以隔離有問題的程式碼區域。 您可能需要多次重複此過程才能縮小範圍。
當您找到有問題的程式碼區域時,請使用偵錯工具進行調查。 若要找出問題的原因,請在偵錯工具中執行應用程式時檢查問題程式碼:
檢查變數 並檢查它們是否包含它們應該包含的值類型。 如果您找到不正確的值,請找出錯誤值的設定位置 (若要找出值的設定位置,您可能需要重新啟動偵錯工具、查看 呼叫堆疊,或兩者) 。
檢查您的應用程式是否正在執行您預期的程式碼。 (例如,在範例應用程式中,我們預期陳述式的
switch程式碼會將星系類型設定為 Irregular,但應用程式因為拼字錯誤而略過程式碼。
小提示
您可以使用偵錯工具來協助您尋找錯誤。 偵錯工具只有在知道程式碼意圖時才能 為您尋找 錯誤。 只有當您(開發人員)表達該意圖時,工具才能知道程式碼的意圖。 編寫 單元測試 就是你做到這一點的方式。
後續步驟
在本文中,您已瞭解一些一般偵錯概念。 接下來,您可以開始深入瞭解偵錯工具。