Lab 01:不用 Agent Mode,使用 GitHub Copilot 完成程式碼重構
透過註解、Code Completion 與 Next Edit Suggestions 改寫 C# 程式
預估時間:25–35 分鐘(不含環境安裝)
使用工具:Visual Studio Code、GitHub Copilot、.NET SDK
對應影片:
Lab 情境與目標
你將從 GitHub 下載一支計算蘋果折扣價格的程式,先用 Copilot 補上香蕉與榴槤的計算。三種水果各有自己的單價及折扣,但會重複使用相同公式。
這次要保留計算結果,將共用公式移到 Fruit 類別,再讓原本的程式改用這個類別。
完成 Lab 後,你將能夠:
- 在程式碼註解中描述需求,讓 Copilot 提供程式碼建議。
- 判斷何時接受或拒絕建議。
- 使用 Next Edit Suggestions 協助修改相關程式碼。
- 執行程式,確認重構前後的結果相同。
本 Lab 的提示詞都輸入在 Program.cs 的註解中,操作重點放在編輯器內的建議功能。
Copilot 每次產生的內容可能不同。請以每一步的檢查點為準,不需要追求與影片完全相同的按鍵次數或程式碼排版。
步驟 1:準備環境
1-1. 確認工具
請先準備:
- Visual Studio Code。
- Git。
- .NET 8 SDK(Repository 的目標框架為
net8.0)。 - VS Code 的 C# 開發支援,例如 Microsoft 的 C# Dev Kit。
- 已登入 GitHub,且能使用 GitHub Copilot 的程式碼建議功能。
在終端機執行:
git --version
dotnet --list-sdks
檢查點: 可以看到 Git 版本,以及 8.x 的 .NET SDK。若只安裝較新的 SDK,請一併確認電腦具有 .NET 8 Runtime,才能執行這個 net8.0 專案。若找不到指令,請先安裝對應工具,再重新開啟 VS Code 與終端機。
1-2. 啟用建議功能
- 開啟 VS Code 的 Settings(設定)。
- 搜尋
github.copilot.enable,確認 C# 的程式碼建議已啟用。 - 搜尋
github.copilot.nextEditSuggestions.enabled,啟用 Next Edit Suggestions。 - 開啟 C# 檔案時,確認 Copilot 沒有處於暫停建議(Snooze)的狀態。
本 Lab 會用到兩種建議:
| 畫面中的建議 | 用途 | 操作方式 |
|---|---|---|
| 游標旁的灰色文字(Ghost Text/Code Completion) | 補上目前位置的程式碼 | 檢查後按 Tab 接受 |
| 下一處修改建議(Next Edit Suggestions,NES) | 根據已經做出的修改,建議其他位置也如何調整 | 依畫面提示按 Tab 前往,再按 Tab 接受 |
不適合的建議可按 Esc 關閉。若 Tab 只是插入空白,請先確認畫面上真的有建議,以及游標焦點在程式碼編輯區。
功能與設定可參考 VS Code:Inline suggestions。
步驟 2:從 GitHub 取得起始專案
2-1. Clone Repository
在準備放置練習專案的位置,開啟終端機並依序執行:
git clone https://github.com/isdaviddong/HOL_Copilot_First.git
cd HOL_Copilot_First
code .
若電腦無法辨識 code 指令,請改由 VS Code 的 File → Open Folder 開啟剛才下載的 HOL_Copilot_First 資料夾。
2-2. 確認專案內容
在 VS Code 的檔案總管確認可以看到:
| 檔案 | 用途 |
|---|---|
Program.cs |
本 Lab 要編輯的 C# 程式 |
folder.csproj |
Console 專案設定,目標框架為 net8.0 |
Repository 的起始 Program.cs 只有蘋果的計算。打開檔案確認即可,接下來直接在這份程式上操作:
//計算蘋果
double applePrice = 35; // 蘋果的價格
double appleDiscount = 0.1; // 蘋果的折扣
Console.WriteLine("蘋果折扣後價格是:" + applePrice * (1 - appleDiscount));
來源:起始 Program.cs、folder.csproj。
2-3. 執行起始程式
選擇 Terminal → New Terminal,確認終端機所在資料夾包含 folder.csproj,接著執行:
dotnet run
預期結果:
蘋果折扣後價格是:31.5
dotnet run 會建置並執行目前的專案,參考 Microsoft Learn:dotnet run。
步驟 3:用 Copilot 補上香蕉的計算
3-1. 在起始程式後面輸入註解
- 開啟
Program.cs,保留原本的蘋果計算程式碼。 - 將游標移到檔案最後,留一個空白行。
- 輸入下方註解,再按
Enter:
// 計算香蕉
稍候,觀察 Copilot 的灰色文字建議。確認使用的是香蕉的變數、價格 20 與折扣 0.15,再按 Tab 接受。
參考結果:
// 計算香蕉
double bananaPrice = 20;
double bananaDiscount = 0.15;
Console.WriteLine("香蕉折扣後價格是:" + bananaPrice * (1 - bananaDiscount));
如果沒有建議,可先輸入 double bananaPrice = ,停下來觀察;仍未出現時,手動補上參考程式碼,再繼續練習。
3-2. 執行並確認
儲存檔案,在終端機執行:
dotnet run
預期結果:
蘋果折扣後價格是:31.5
香蕉折扣後價格是:17
本 Lab 中,Discount = 0.1 表示減價 10%,也就是支付原價的 90%。計算公式為:
折扣後價格 = 單價 × (1 - 折扣率)
步驟 4:用註解讓 Copilot 補上榴槤的計算
對應影片:約 00:30–01:05
4-1. 輸入註解
- 將游標移到
Program.cs最後一行的下一行。 - 留一個空白行。
- 輸入下方註解,再按
Enter換行:
// 計算榴槤
這裡補上價格與折扣,讓各位學員使用一致的練習資料。
4-2. 檢查並接受建議
稍候,觀察 Copilot 是否顯示灰色文字。接受之前先確認:
- 變數代表榴槤,沒有誤用蘋果或香蕉的變數。
- 單價為
100,折扣率為0.2。 - 公式為單價乘上
1 - 折扣率。 - 輸出的水果名稱為「榴槤」。
內容正確就按 Tab 接受。若一次只補完一部分,可繼續換行或輸入程式碼前綴,再檢查下一次建議。
參考結果:
// 計算榴槤
double durianPrice = 100;
double durianDiscount = 0.2;
Console.WriteLine("榴槤折扣後價格是:" + durianPrice * (1 - durianDiscount));
如果沒有建議,可先輸入 double durianPrice = ,停下來觀察;仍未出現時,手動補上參考程式碼,再繼續後面的練習。
4-3. 記錄重構前的結果
儲存後執行 dotnet run。
| 水果 | 單價 | 折扣率 | 預期折扣後價格 |
|---|---|---|---|
| 蘋果 | 35 | 0.1 | 31.5 |
| 香蕉 | 20 | 0.15 | 17 |
| 榴槤 | 100 | 0.2 | 80 |
檢查點: 三筆結果都正確。接下來的重構必須保留這些結果。
步驟 5:用提示詞產生 Fruit 類別
對應影片:約 01:05–02:39
5-1. 先想清楚要抽出什麼
觀察目前的三段程式碼:水果名稱、單價及折扣不同,但計算方式相同。
先花一分鐘,試著自己寫一句註解,說明這個類別應該有哪些資料,以及能做什麼。
5-2. 在檔案最下方加入需求
請將游標移到三段水果計算程式碼的後面,輸入:
// 一個計算水果折扣的類別,具有水果名稱、單價、折扣三個屬性,和一個計算折扣後價格的方法。
按 Enter,等待 Copilot 的建議。這就是影片示範的做法:將需求寫進程式碼註解。
若產生的名稱或計算方式不明確,可把註解補充成:
// 建立 Fruit 類別。
// Name 是 string,預設為空字串;Price 與 Discount 是 double。
// Discount 是減價比例,例如 0.1 表示減價 10%。
// GetDiscountedPrice() 回傳 Price * (1 - Discount)。
如果仍沒有建議,先輸入 class Fruit 並換行,再觀察後續補完。
5-3. 接受並檢查類別
以下是符合需求的一種寫法,先自行操作,再用它核對:
class Fruit
{
public string Name { get; set; } = string.Empty;
public double Price { get; set; }
public double Discount { get; set; }
public double GetDiscountedPrice()
{
return Price * (1 - Discount);
}
}
檢查點:
- 類別具有
Name、Price、Discount三個屬性。 GetDiscountedPrice()回傳折扣後的價格。- 沒有把
0.1誤解為只支付原價的 10%。 Fruit類別放在檔案最下方,所有直接執行的敘述都在類別前面。
本 Lab 使用 Console 範本的 top-level statements 寫法。請把
Console.WriteLine(...)等直接執行的程式放在class Fruit前面,避免出現 CS8803。
5-4. 儲存並執行
執行 dotnet run,確認依然得到 31.5、17、80。
此時只是新增類別,上方三段程式還沒有使用它,所以還沒完成重構。
步驟 6:先把蘋果改成使用 Fruit
對應影片:約 03:15–03:52
6-1. 在蘋果的變數後面加入物件
找到這兩行:
double applePrice = 35;
double appleDiscount = 0.1;
在 appleDiscount 那行後面、原本的 Console.WriteLine 前面插入一行,開始輸入:
var apple = new Fruit
等待 Copilot 補完物件初始化內容。確認名稱、單價及折扣正確後,再按 Tab 接受。
這一步的目標:
var apple = new Fruit
{
Name = "蘋果",
Price = applePrice,
Discount = appleDiscount
};
Copilot 可能把整段寫在同一行,效果相同。
6-2. 觀察下一處修改建議
接受上述修改後,觀察 Copilot 是否建議把下方原本的運算式:
applePrice * (1 - appleDiscount)
改成:
apple.GetDiscountedPrice()
如果出現 Next Edit Suggestions,依畫面提示前往該位置,確認內容後接受建議。有時第一次 Tab 是跳到建議位置,第二次才是套用修改。
如果沒有出現,手動將蘋果那行改成:
Console.WriteLine("蘋果折扣後價格是:" + apple.GetDiscountedPrice());
檢查點: 蘋果的價格已經透過 Fruit.GetDiscountedPrice() 計算,而且只輸出一次。
步驟 7:讓相同修改延伸到香蕉與榴槤
對應影片:約 03:53–04:27
7-1. 觀察 Copilot 是否提出其他修改
完成蘋果的修改後,觀察香蕉與榴槤附近是否出現建議。
如果出現,逐一檢查再接受;如果沒有,將游標移到對應折扣變數後面,開始輸入 var banana = new Fruit 或 var durian = new Fruit,讓 Copilot 接續補完。
7-2. 完成香蕉的修改
在香蕉的折扣變數後面加入物件,並替換原本的輸出敘述:
var banana = new Fruit
{
Name = "香蕉",
Price = bananaPrice,
Discount = bananaDiscount
};
Console.WriteLine("香蕉折扣後價格是:" + banana.GetDiscountedPrice());
7-3. 完成榴槤的修改
同樣在榴槤的折扣變數後面加入:
var durian = new Fruit
{
Name = "榴槤",
Price = durianPrice,
Discount = durianDiscount
};
Console.WriteLine("榴槤折扣後價格是:" + durian.GetDiscountedPrice());
檢查點:
- 三種水果都各自建立了一個
Fruit物件。 - 三個輸出敘述都呼叫各自的
GetDiscountedPrice()。 - 香蕉沒有誤用
applePrice,榴槤也沒有誤用其他水果的物件。 - 原本的直接計算式已被替換,沒有多留一份舊的輸出敘述。
- 共用公式只保留在
Fruit類別的方法中。
步驟 8:執行並驗證重構結果
對應影片:約 04:58–05:10
8-1. 儲存並執行
儲存 Program.cs,在專案資料夾中執行:
dotnet run
預期結果:
蘋果折扣後價格是:31.5
香蕉折扣後價格是:17
榴槤折扣後價格是:80
請與步驟 4 記錄的結果比較。小數點符號可能隨電腦的地區設定不同,數值應一致。
8-2. 再做一次小修改
將:
double appleDiscount = 0.1;
暫時改成:
double appleDiscount = 0.2;
儲存並執行。蘋果應變成 28,香蕉與榴槤仍為 17、80。
這可以幫助你確認類別真的使用傳入的折扣,而不是把結果寫死。檢查完畢後,將蘋果折扣改回 0.1。
8-3. 完成確認
- 我曾在程式碼註解中描述需求,並觀察 Copilot 的建議。
- 我知道如何接受與關閉建議。
- 我檢查過 AI 產生的名稱、變數與計算公式。
- 三種水果都改為使用
Fruit類別。 - 重構後的輸出仍為
31.5、17、80。 - 我修改折扣後,程式能算出正確的新結果。
常見問題
| 遇到的情況 | 處理方式 |
|---|---|
| 沒有灰色文字建議 | 確認已登入 Copilot、C# 建議已啟用,且沒有暫停建議;補上具體註解或程式碼前綴再觀察。 |
| 沒有下一處修改建議 | 確認 NES 設定已啟用;仍未出現就手動移到下一段,依相同方式修改。建議不一定每次都出現。 |
Tab 沒有接受 Copilot 建議 |
確認焦點在編輯器;若 IntelliSense 選單正在攔截按鍵,可先關閉選單,再確認 Copilot 建議是否仍顯示。 |
| AI 產生不同的類別或方法名稱 | 可調整提示詞使用本 Lab 的名稱,或自行統一所有呼叫位置;定義與呼叫必須一致。 |
出現 CS8618,指出 Name 未初始化 |
將屬性寫成 public string Name { get; set; } = string.Empty;。 |
| 出現 CS8803 | 把 Fruit 類別放到檔案最後,直接執行的程式放在它前面。 |
dotnet run 找不到專案 |
確認終端機所在資料夾包含 .csproj;必要時先 cd HOL_Copilot_First。 |
| 同一種水果輸出兩次 | 刪除舊的 Console.WriteLine,每種水果只保留改用類別後的那一行。 |
| 計算結果不正確 | 檢查物件使用的變數、折扣值,以及方法是否回傳 Price * (1 - Discount)。 |
完整參考程式碼
完成操作後再展開核對。若卡關,也可以用這份程式碼找出差異。
展開 Program.cs 完整參考內容
// 計算蘋果
double applePrice = 35;
double appleDiscount = 0.1;
var apple = new Fruit
{
Name = "蘋果",
Price = applePrice,
Discount = appleDiscount
};
Console.WriteLine("蘋果折扣後價格是:" + apple.GetDiscountedPrice());
// 計算香蕉
double bananaPrice = 20;
double bananaDiscount = 0.15;
var banana = new Fruit
{
Name = "香蕉",
Price = bananaPrice,
Discount = bananaDiscount
};
Console.WriteLine("香蕉折扣後價格是:" + banana.GetDiscountedPrice());
// 計算榴槤
double durianPrice = 100;
double durianDiscount = 0.2;
var durian = new Fruit
{
Name = "榴槤",
Price = durianPrice,
Discount = durianDiscount
};
Console.WriteLine("榴槤折扣後價格是:" + durian.GetDiscountedPrice());
// 共用的水果折扣計算類別
class Fruit
{
public string Name { get; set; } = string.Empty;
public double Price { get; set; }
public double Discount { get; set; }
public double GetDiscountedPrice()
{
return Price * (1 - Discount);
}
}
操作後想一想
- 只寫「幫我建立水果類別」,和明確寫出屬性、折扣定義及方法,產生的建議有什麼差別?
- 在這次重構中,哪些決定由你負責?哪些修改交給 Copilot 協助?
- 如果 AI 產生的程式可以編譯,但折扣公式錯了,你會怎麼發現?
這次練習的重點是:你決定要怎麼改、確認 AI 的建議,再用執行結果檢查修改是否符合需求。