前幾天開始學 Debug 之後,我終於比較知道:
F12
可以找程式在哪
Breakpoint
可以確認程式有沒有跑到
Locals / Watch
可以看資料現在是多少
Call Stack
可以看程式是怎麼一路跑進來的
但實際開發時,還有另一種很常見的情況:
我根本還沒 Debug,
Visual Studio 就先開始噴錯了。
有時是:
整支專案 Build 不起來
有時是:
網站跑起來後才突然跳 Exception
有時候畫面只顯示:
Something went wrong
然後我看 Visual Studio:
Error List 一堆紅字
Output 又一大串
Exception 視窗也跳出來
第一次真的會不知道:
到底要先看哪一個?
後來我才慢慢把這三個東西分開。
我現在會先記成:
Error List
看「編譯時有哪些錯誤」
Output
看「系統實際做了什麼、詳細訊息在哪」
Exception
看「程式執行到哪裡炸掉」
這篇就來把三個真的分清楚。
我以前看到錯誤時,很容易:
Error List 看一下
↓
Output 看一下
↓
瀏覽器看一下
↓
Google 錯誤訊息
↓
又回來亂改程式
結果其實連:
這是編譯錯誤
還是執行錯誤
都還沒分清楚。
所以現在我會先問:
程式是根本 Build 不起來?
還是 Build 成功,
但跑起來才出錯?
這兩種要看的地方其實不一樣。
例如我改完程式,Visual Studio 顯示:
Build Failed
這時通常第一個先看:
Error List
也就是:
錯誤清單
可以從 Visual Studio:
View
↓
Error List
打開。

Error List 最常拿來看:
編譯器現在認為哪裡有問題

例如:
public IActionResult Detail(int id)
{
var model = _userService.GetDetail(id)
return View(model);
}
這裡少了一個:
;
Build 時就可能出現錯誤。
Error List 可能會顯示類似:
; expected
而且會附上:
Project
File
Line
所以我可以直接知道:
哪個專案
哪支檔案
哪一行
出問題。
Error List 裡通常不只有:
Error
還會看到:
Warning
Message
錯誤
通常代表:
這個問題會讓目前 Build 無法正常完成
例如:
語法錯誤
找不到型別
找不到方法
參數型別不符
變數不存在
警告
通常代表:
程式可能還是可以 Build,
但有值得注意的地方
例如可能看到:
可能為 null
變數宣告後沒有使用
某個 API 已經過時
Warning 不代表:
可以全部無視
只是它通常和:
Build 直接失敗
不是同一個等級。
比較偏:
編譯器或分析工具提供的其他資訊
新手階段不用每一個都研究。
如果 Build Failed,我現在會先把篩選放在:
Errors
先處理真正阻止 Build 的東西。
這個真的很實用。
假設 Error List 一次出現:
20 個 Error
我以前會覺得:
完了,壞 20 個地方。
但不一定。
例如我前面漏一個:
}
可能讓後面整支檔案都被編譯器誤判。
結果一次產生很多錯誤。
所以我現在通常會:
先看最前面的 Error
尤其是:
最早發生的語法錯誤
先修掉。
然後重新 Build。
很可能:
20 個 Error
↓
修 1 個
↓
剩 0 個
所以不要看到錯誤數量很多,就從中間隨便挑一個修。
例如 Error List 顯示:
CS0103
The name 'user' does not exist in the current context
雙擊這筆錯誤。
Visual Studio 通常會直接跳到:
發生錯誤的程式碼位置
例如:
model.Name = user.Name;
但前面根本沒有:
var user = ...
這種錯誤就很好處理。
所以:
Build Failed
↓
Error List
↓
雙擊第一個 Error
↓
看程式碼
↓
修正
↓
重新 Build
這是我現在最基本的處理方式。
Output 中文通常叫:
輸出
它比 Error List 更像:
Visual Studio 和相關工具在執行某個動作時,留下的詳細紀錄。
可以從:
View
↓
Output 輸出
打開。

Output 上方通常還能選:
Show output from:
例如:
Build
Debug
因為兩個角度不太一樣。
Error List 比較像:
幫我整理過的錯誤清單
Output 則比較像:
這次 Build 從頭到尾到底做了什麼
例如 Error List 可能只看到:
Build failed
或一筆錯誤。
但 Output 可能會顯示:
正在建置哪個 Project
Restore 做了什麼
編譯哪個檔案
哪一個步驟失敗
最後 Build 成功幾個、失敗幾個
在 Solution 裡有很多 Project 時,這特別有用。
例如:
WebSite
Service
Data
Common
按 Build Solution 後失敗。
我想知道:
到底是哪個 Project 先掛掉?
Output 通常會比 Error List 更容易看完整流程。
我現在會這樣用。
我只想快速知道:
哪支程式哪一行錯?
↓
Error List
我想知道:
Build 到底在哪個步驟失敗?
哪個 Project 失敗?
工具實際輸出了什麼?
↓
Output
所以不是:
Error List 比 Output 好
或:
Output 才是專業
而是它們回答的問題不同。
假設 Controller 寫:
public IActionResult Detail()
{
UserDetailViewModel model = new UserDetailViewModel();
return View(model);
}
但專案裡根本找不到:
UserDetailViewModel
Build 時 Error List 可能看到:
The type or namespace name 'UserDetailViewModel' could not be found
這時我會先檢查:
這個 class 真的存在嗎?
namespace 對嗎?
有沒有缺 using?
是不是少了 Project Reference?
新檔案有沒有真的加進專案?
如果還看不出來,再到 Output 看:
是哪個 Project 編譯時出問題
這比看到錯誤直接:
重裝 Visual Studio
實際太多。
這種情況就跟 Error List 不太一樣了。
例如:
public IActionResult Detail(int id)
{
var user = _userService.GetUser(id);
var name = user.Name;
return View();
}
程式可以 Build。
因為語法沒問題。
但執行時:
user = null
接著跑:
user.Name
就炸了。
這種是:
Runtime Error
執行時錯誤
這時可能會看到:
Exception
Exception 可以先理解成:
程式執行過程中發生了無法正常繼續處理的異常狀況。
例如:
NullReferenceException
可能代表:
我正在對 null 物件取屬性或呼叫方法
像:
var user = null;
var name = user.Name;
就可能發生:
NullReferenceException
這種錯誤不是編譯器能事先完全知道的。
因為真正執行時:
user 到底有沒有資料
要跑了才知道。
如果正在 Debug,Visual Studio 可能直接停在:
發生 Exception 的那一行
例如:
var name = user.Name;
然後跳出類似:
System.NullReferenceException
這時候最重要的不是先把整段 Exception 全貼給 AI。
我會先看:
Exception Type 是什麼?
停在哪一行?
這一行有哪些變數?
哪個東西可能是 null?
例如:
System.NullReferenceException
下面還會有 Message。
或者:
InvalidOperationException
SqlException
ArgumentNullException
不同 Exception 會提供不同資訊。
像 SQL 問題可能看到:
Invalid column name ...
或:
Login failed ...
這時就能知道問題不是:
View
而可能是:
SQL
Database
Connection
所以 Exception 發生時,我現在至少會抓三個資訊:
Exception Type
Exception Message
發生位置
這三個通常比只說:
程式壞了
有用非常多。
有時候看到 Exception:
外層看起來只說:
Something failed
但真正原因藏在:
Inner Exception
可以先理解成:
這個 Exception 是因為另一個更底層的 Exception 引起的。
例如:
Service 發生錯誤
↓
底層其實是 SQL Connection 失敗
外層可能是一個:
InvalidOperationException
但 Inner Exception 才寫:
Login failed for user...
所以如果看到錯誤訊息很籠統,我會開始找:
InnerException
看看裡面有沒有更接近真正原因的訊息。
Exception 通常還會看到:
Stack Trace
這跟昨天的:
Call Stack
概念有點接近。
它會告訴我:
這個錯誤經過哪些方法一路發生
例如可能類似:
UserRepository.GetById()
UserService.GetDetail()
UserController.Detail()
我就知道:
錯誤是在 Repository 發生
一路往 Service、Controller 傳上來
所以看到很長的 Stack Trace 時,不用從頭到尾每一行都懂。
新手階段先找:
自己專案的 namespace
自己寫的 class
自己寫的方法
例如:
UserService.cs: line 52
這通常比:
Microsoft.AspNetCore...
System.Runtime...
更值得先看。
這是最煩的一種。
例如:
Build 成功
沒有 Exception
網站也可以開
但畫面:
部門名稱顯示錯了
或者:
按鈕按下去沒有更新資料
這時:
Error List
可能是空的
Output
也不一定有明顯 Error
Exception
也完全沒有
因為程式沒有「壞到不能執行」。
只是:
邏輯寫錯
這時就要回到前幾天:
Breakpoint
Locals
Watch
Call Stack
去 Debug。
所以這三種情況可以先分成:
Build 失敗
↓
Error List / Output
執行時炸掉
↓
Exception / Stack Trace / Debug
程式正常跑但結果不對
↓
Breakpoint / Locals / Watch
這個分類我覺得超重要。
程式執行時,Output 也可能看到 Debug 資訊。
例如:
Microsoft.AspNetCore...
一些 Framework 訊息。
或程式自己寫:
Debug.WriteLine("進入 GetDetail");
也可能在 Debug Output 看到。
實際專案如果有 Logging:
LogInformation
LogWarning
LogError
則紀錄的位置可能依專案設定不同。
有些在:
Visual Studio Output
有些在:
Console
Log File
Application Insights
其他 Logging 平台
所以不要把 Output 想成:
所有 Log 永遠都在這
還是要看專案怎麼設定。
不一定。
例如 Error List 可能顯示:
某個 ViewModel 找不到
但真正原因可能是:
前面某支 Project Build 失敗
導致後面的 Project 無法取得它。
所以多專案 Solution 裡,我現在會注意:
第一個真正失敗的是哪個 Project?
這時 Output 就很重要。
可以先找:
Build FAILED
附近的訊息。
而不是只看 Error List 最後一筆。
我會按這個順序。
第一步
看 Error List
↓
第二步
先找第一個真正的 Error
↓
第三步
雙擊跳到程式碼
↓
第四步
如果看不懂原因
再看 Output
↓
第五步
確認是哪個 Project / Build Step 先失敗
↓
第六步
修完重新 Build
不是:
看到 30 個 Error
↓
30 個全部 Google
如果程式已經跑起來才出錯:
第一步
看 Exception Type
↓
第二步
看 Message
↓
第三步
看停在哪一行
↓
第四步
看那一行相關變數
↓
第五步
Locals / Watch
↓
第六步
需要時看 Call Stack / Stack Trace
↓
第七步
往前找資料在哪一步開始異常
例如:
NullReferenceException
不要第一時間:
?.
全部補一補。
先搞清楚:
到底是哪個物件是 null?
它為什麼會是 null?
它本來應不應該是 null?
這比較重要。
| 現象 | 我會先看哪裡 |
|---|---|
| Build Failed | Error List |
| 錯誤很多,看不出根因 | Output |
| 想知道哪個 Project Build 失敗 | Output |
| 程式跑到一半直接炸 | Exception |
| 想知道 Exception 發生在哪 | Exception + Stack Trace |
| 畫面錯但完全沒報錯 | Breakpoint / Locals / Watch |
| 不知道這次怎麼跑到某方法 | Call Stack |
我現在會這樣記:
Error List
↓
編譯器整理給我的錯誤清單
↓
「哪支檔案哪一行不能編譯?」
Output
↓
Visual Studio / Build / Debug 的詳細執行紀錄
↓
「剛剛到底做了什麼?」
Exception
↓
程式執行過程發生異常
↓
「程式跑到哪裡炸掉了?」
三個不是互相取代。
而是:
不同階段
看不同東西
以前我可能直接丟:
為什麼壞掉?
甚至只截一張整個 Visual Studio 的畫面。
現在如果要問 AI,我會盡量整理成:
1. 我現在是在 Build 還是 Runtime 出錯?
2. Error / Exception 的完整名稱
3. 最重要的錯誤訊息
4. 發生的程式碼位置
5. 相關程式碼
6. 我原本預期什麼結果
例如:
ASP.NET Core MVC 專案 Build 成功,
但執行 UserController.Detail() 時發生
NullReferenceException。
程式停在:
var name = user.Name;
Debug 看到 user = null。
user 來自:
_userService.GetUser(id);
我接下來應該從哪裡檢查?
這種問題比:
C# 出錯了怎麼辦
有效很多。
AI 也比較不容易亂猜。
以前看到錯誤:
怎麼修?
現在我會先問:
它是哪一種問題?
例如:
Build 就失敗
↓
先查編譯
Build 成功但 Runtime 炸
↓
查 Exception
程式都沒炸但結果錯
↓
查流程和資料
先分類,後面才知道工具要用哪個。
這對我來說比背很多 Error Code 還重要。
Visual Studio 報錯時,不要第一時間看到哪裡紅就亂改哪裡。
先判斷:
它發生在哪個階段?
如果:
程式根本 Build 不起來
先看:
Error List
快速找到:
哪支檔案
哪一行
什麼編譯錯誤
如果錯誤很多或想知道完整 Build 過程:
Output
看:
哪個 Project
哪個步驟
真正第一個失敗點
如果:
Build 成功
但程式跑起來才炸
看:
Exception Type
Message
發生位置
Stack Trace
再配合:
Locals
Watch
Call Stack
找資料為什麼不對。
所以我現在會記成:
Build 不過
↓
Error List / Output
Runtime 炸掉
↓
Exception / Stack Trace
沒炸但結果錯
↓
Breakpoint / Locals / Watch
最重要的不是:
看到錯誤馬上修
而是先搞清楚:
到底是哪一類錯誤?
真正的第一個異常出在哪裡?
到這裡,前面幾天的 Debug 工具終於可以完整串起來了:
F12
找到程式
↓
Breakpoint
確認有沒有執行
↓
Locals / Watch
確認資料
↓
Call Stack
確認執行路線
↓
Error List / Output / Exception
確認錯誤發生在哪個階段
下一篇開始,就可以離開 Debug,進到另一個工作上每天都會碰的東西:
程式終於改完了,但在 Commit 之前,我到底要怎麼確認自己改了哪些檔案、哪些行,以及有沒有不小心把不該送出去的東西一起改進去?