在上一節課中,我學習了型別轉換、算術運算子、關係運算子、邏輯運算子以及各種分支結構,瞭解了這些語法的含義和執行過程。今天的課程聚焦於如何讓我們編寫的程式更具穩固性,減少出錯的可能性。我學習了另一種分支結構 switch - case,並將其與上節課所學的分支結構進行了對比。此外,還學習了迴圈結構,這是目前所學內容中較為重要且複雜的一部分。為了更好地吸收和理解這些知識,我放慢了學習腳步,透過不斷編寫程式碼來鞏固和加深理解。下面是我這節課所學的具體內容:
一、例外捕獲
在護理工作中,我們經常需要處理各種意外情況。比如給病人測量體溫時溫度計可能故障,輸液時可能會出現堵管等情況。同樣,在程式設計中,我們也需要處理各種例外情況。C#提供了例外處理機制來幫助我們優雅地處理這些問題。
1. try-catch 基本語法
try
{
// 可能出現例外的程式碼
}
catch (Exception ex)
{
// 處理例外的程式碼
}
2. 實際應用範例
以下是一個在護理工作中錄入病人生命徵象資料的範例:
try
{
Console.Write("請輸入病人體溫: ");
double temperature = Convert.ToDouble(Console.ReadLine());
if (temperature < 35 || temperature > 42)
{
throw new Exception("體溫資料異常,請重新檢查");
}
Console.Write("請輸入收縮壓: ");
int systolicPressure = Convert.ToInt32(Console.ReadLine());
if (systolicPressure < 60 || systolicPressure > 200)
{
throw new Exception("血壓資料異常,請重新測量");
}
Console.WriteLine($"記錄的體溫為: {temperature}°C");
Console.WriteLine($"記錄的收縮壓為: {systolicPressure}mmHg");
}
catch (FormatException)
{
Console.WriteLine("輸入格式錯誤,請輸入有效的數字");
}
catch (OverflowException)
{
Console.WriteLine("輸入的數值超出範圍");
}
catch (Exception ex)
{
Console.WriteLine($"發生錯誤: {ex.Message}");
// 可以在這裡記錄錯誤日誌
}
finally
{
Console.WriteLine("資料錄入操作完成");
// 無論是否發生例外,都會執行的清理工作
}
3. 常見例外型別
在護理資訊系統中,我們經常會遇到以下幾種例外:
- FormatException: 當輸入的資料格式不正確時擲出,如輸入字母而不是數字
- OverflowException: 當數值超出型別範圍時擲出
- ArgumentException: 當參數值不符合要求時擲出
- NullReferenceException: 試圖存取空物件時擲出
4. 自訂例外
有時我們需要根據業務邏輯擲出自訂例外:
public class VitalSignException : Exception
{
public VitalSignException(string message) : base(message)
{
}
}
try
{
int heartRate = 150;
if (heartRate > 120)
{
throw new VitalSignException("心率異常升高,需要立即處理!");
}
}
catch (VitalSignException ex)
{
Console.WriteLine($"生命徵象異常: {ex.Message}");
// 這裡可以新增緊急處理流程
}
5. 最佳實務
- 只捕獲預期的例外,避免捕獲所有例外
- 在適當的層級處理例外
- 記錄例外資訊以便後續分析
- 使用 finally 區塊進行清理工作
- 提供有意義的錯誤資訊
二、變數的範圍
變數的範圍指的是能夠使用該變數的範圍。就像在醫院裡,不同科別的護士只能查看自己科別的病人資訊一樣,變數也有其使用範圍的限制。
1. 區域變數
區域變數的範圍從宣告它的括號開始,到該括號對應的結束括號結束。這就像護理站內部使用的暫存記錄單:
void RecordPatientVitals()
{
// temperature 只在這個方法內有效
double temperature = 36.5;
if (temperature > 37.3)
{
// fever 只在if陳述式區塊內有效
string fever = "發燒";
Console.WriteLine(fever);
}
// 這裡無法使用 fever 變數
{
// pulse 只在這個程式碼區塊內有效
int pulse = 80;
}
// 這裡無法使用 pulse 變數
}
2. 類別層級變數(成員變數)
類別層級變數的範圍在整個類別內部都是可見的,就像病房的護理記錄單可以被所有班次的護士存取:
public class Patient
{
// 這些變數在整個類別中都可以存取
private string patientName;
private int patientAge;
private string bedNumber;
public void AdmitPatient(string name, int age)
{
patientName = name; // 可以存取類別層級變數
patientAge = age; // 可以存取類別層級變數
}
public void AssignBed(string bed)
{
bedNumber = bed; // 可以存取類別層級變數
}
}
3. 全域變數(靜態變數)
靜態變數可以被整個程式存取,類似於醫院的通用規章制度:
public class HospitalConstants
{
// 這些靜態變數可以在任何地方存取
public static readonly double NORMAL_TEMPERATURE = 37.0;
public static readonly int NORMAL_SYSTOLIC_PRESSURE = 120;
public static readonly int NORMAL_DIASTOLIC_PRESSURE = 80;
}
public class NursingRecord
{
public void CheckTemperature(double temperature)
{
// 可以在任何類別中存取 HospitalConstants 的靜態變數
if (temperature > HospitalConstants.NORMAL_TEMPERATURE)
{
Console.WriteLine("體溫偏高");
}
}
}
4. 變數遮蔽
當區域變數和類別層級變數同名時,區域變數會「遮蔽」類別層級變數:
public class VitalSigns
{
private double temperature = 36.5; // 類別層級變數
public void UpdateTemperature(double temperature) // 參數
{
// 這裡的 temperature 指的是參數,而不是類別層級變數
Console.WriteLine($"新的體溫: {temperature}");
// 使用 this 關鍵字存取類別層級變數
this.temperature = temperature;
}
}
5. 範圍的最佳實務
- 變數的範圍應該儘可能小,這樣可以減少出錯的可能
- 避免使用全域變數,因為它們可能被任何程式碼修改
- 使用有意義的變數名稱,反映其用途
- 及時釋放不再使用的資源
- 注意變數的生命週期,避免存取已經超出範圍的變數
三、switch - case 陳述式
在護理工作中,我們經常需要根據不同的情況作出不同的處理決定。switch - case 陳述式就像我們在護理工作中使用的標準處理流程,根據不同的情況選擇相應的處理方案。
1. 支援的型別
switch 陳述式支援以下型別的表達式:
- 整數型別(
int、long、byte等) - 字元型別(
char) - 字串型別(
string) - 列舉型別(
enum) - 布林型別(
bool)- C# 7.0 及以上版本 - 模式比對(C# 7.0 及以上版本)
- 型別模式
- 常數模式
- var 模式
- 屬性模式(C# 8.0 及以上)
範例:
// 列舉型別範例
enum PatientStatus
{
Normal,
Fever,
Pain,
Critical
}
PatientStatus status = PatientStatus.Fever;
switch (status)
{
case PatientStatus.Normal:
Console.WriteLine("繼續觀察");
break;
case PatientStatus.Fever:
Console.WriteLine("進行降溫處理");
break;
case PatientStatus.Pain:
Console.WriteLine("給予止痛治療");
break;
case PatientStatus.Critical:
Console.WriteLine("立即通知醫生");
break;
}
// 模式比對範例(C# 7.0+)
object obj = "護理記錄";
switch (obj)
{
case string s:
Console.WriteLine($"這是一個字串:{s}");
break;
case int n:
Console.WriteLine($"這是一個整數:{n}");
break;
case null:
Console.WriteLine("物件為空");
break;
default:
Console.WriteLine("未知型別");
break;
}
2. 基本語法
switch (表達式)
{
case 常數1:
陳述式1;
break;
case 常數2:
陳述式2;
break;
default:
預設陳述式;
break;
}
3. 實際應用範例
例如,根據病人的疼痛等級採取不同的護理措施:
int painLevel = 3; // 疼痛等級(0-10)
switch (painLevel)
{
case 0:
Console.WriteLine("無需止痛處理");
break;
case 1:
case 2:
case 3:
Console.WriteLine("建議非藥物治療,如按摩、熱敷等");
break;
case 4:
case 5:
case 6:
Console.WriteLine("考慮口服止痛藥");
break;
case 7:
case 8:
case 9:
case 10:
Console.WriteLine("需要立即處理,考慮注射止痛藥");
break;
default:
Console.WriteLine("無效的疼痛等級");
break;
}
4. 注意事項
- break 陳述式的重要性:每個 case 分支都必須以 break 結束,否則會繼續執行下一個 case
- case 的合併:多個 case 可以共用同一個處理邏輯,如範例中的疼痛等級分組
- default 分支:用於處理所有未明確指定的情況,類似於護理中的緊急應變措施
5. 分支結構的對比
if、if-else 和 switch 的區別
條件型別:
if:可以判斷任何回傳布林值的條件if-else:同樣可以判斷任何布林條件,但提供了替代方案switch:只能判斷相等性,且要求使用常數表達式
適用場景:
if:適合單一條件判斷if-else:適合兩種及以上情況的判斷switch:適合多個等值條件的判斷
效能考量:
- 當分支較少時,三者效能相近
- 當分支較多時,
switch通常比多個if-else效能更好,因為編譯器會最佳化成跳躍表
if-else if 和 switch-case 的詳細對比
// 使用 if-else if
if (patientStatus == "發燒")
{
Console.WriteLine("進行物理降溫");
CheckTemperature();
}
else if (patientStatus == "疼痛")
{
Console.WriteLine("評估疼痛等級");
PainAssessment();
}
else if (patientStatus == "出血")
{
Console.WriteLine("立即止血");
StopBleeding();
}
else
{
Console.WriteLine("繼續觀察");
}
// 使用 switch-case
switch (patientStatus)
{
case "發燒":
Console.WriteLine("進行物理降溫");
CheckTemperature();
break;
case "疼痛":
Console.WriteLine("評估疼痛等級");
PainAssessment();
break;
case "出血":
Console.WriteLine("立即止血");
StopBleeding();
break;
default:
Console.WriteLine("繼續觀察");
break;
}
主要區別:
語法結構:
if-else if結構更靈活,可以處理複雜的條件判斷switch-case結構更規範,程式碼更整潔
條件限制:
if-else if可以使用任何條件表達式switch-case只能使用相等性比較
執行流程:
if-else if會逐個判斷條件switch-case直接跳轉到匹配的 case
程式碼維護:
- 當分支較多時,
switch-case的可讀性和可維護性通常更好 if-else if適合處理複雜的邏輯判斷
- 當分支較多時,
效能考量:
- 對於大量分支的情況,
switch-case通常效能更好 - 對於少量分支,兩者效能差異不大
- 對於大量分支的情況,
6. 使用建議
- 當需要根據一個變數的不同值執行不同操作時,優先使用 switch-case
- 當分支較多時,switch-case 的可讀性通常優於 if-else
- 對於複雜的條件判斷(如範圍判斷),使用 if-else 更合適
- 確保所有可能的情況都有相應的處理邏輯
四、迴圈結構
在護理工作中,我們經常需要重複執行某些操作,比如每小時測量一次生命徵象,或者給病房的每個病人查房。在程式設計中,迴圈結構就是用來處理這種重複性工作的。
1. while 迴圈
while 迴圈會在條件為真時重複執行程式碼區塊。就像我們在護理工作中,需要持續監測病人的體溫直到體溫恢復正常:
double temperature = 39.0;
while (temperature > 37.3)
{
Console.WriteLine($"目前體溫:{temperature}°C,需要繼續降溫");
// 模擬降溫處理
temperature -= 0.2;
Console.WriteLine("進行物理降溫...");
Thread.Sleep(1000); // 模擬等待一段時間
}
Console.WriteLine("體溫已恢復正常");
2. do-while 迴圈
do-while 迴圈至少會執行一次程式碼區塊,然後再判斷條件。這類似於我們必須先給病人測量一次體溫,然後才能決定是否需要繼續監測:
int painLevel;
do
{
Console.WriteLine("請評估疼痛等級(0-10):");
painLevel = Convert.ToInt32(Console.ReadLine());
if (painLevel > 0)
{
Console.WriteLine("實施止痛措施...");
// 進行止痛處理
}
} while (painLevel > 3); // 當疼痛等級大於3時繼續監測
3. for 迴圈
for 迴圈通常用於知道具體迴圈次數的情況。比如查房時需要檢查每個病床的病人:
int bedCount = 6; // 假設病房有6張床
for (int bedNumber = 1; bedNumber <= bedCount; bedNumber++)
{
Console.WriteLine($"正在查看{bedNumber}號床的病人");
// 進行查房操作
CheckPatientStatus(bedNumber);
}
4. foreach 迴圈
foreach 迴圈用於走訪集合中的每個元素。例如,查看所有待處理的護理任務:
List<string> nursingTasks = new List<string>
{
"測量生命徵象",
"更換藥液",
"傷口護理",
"病歷記錄"
};
foreach (string task in nursingTasks)
{
Console.WriteLine($"執行護理任務:{task}");
// 執行護理任務
PerformNursingTask(task);
}
5. 迴圈控制陳述式
- break 陳述式:立即退出迴圈
while (true)
{
double temperature = MeasureTemperature();
if (temperature <= 37.3)
{
Console.WriteLine("體溫正常,停止監測");
break; // 體溫正常時退出迴圈
}
// 繼續監測
}
- continue 陳述式:跳過目前迴圈的剩餘部分,繼續下一次迴圈
for (int bedNumber = 1; bedNumber <= 6; bedNumber++)
{
if (IsBedEmpty(bedNumber))
{
continue; // 如果床位空著,跳過目前迴圈
}
// 對有病人的床位進行護理操作
ProvideNursing(bedNumber);
}
6. 迴圈的最佳實務
選擇合適的迴圈型別:
- 不確定迴圈次數時使用
while - 至少需要執行一次時使用
do-while - 知道具體迴圈次數時使用
for - 走訪集合時使用
foreach
- 不確定迴圈次數時使用
避免無限迴圈:
- 確保迴圈條件最終會變為 false
- 在適當的時候使用 break 陳述式退出迴圈
效能考量:
- 避免在迴圈中進行不必要的計算
- 儘可能減少迴圈巢狀的層數
- 合理使用 continue 陳述式跳過不需要的操作
程式碼可讀性:
- 使用有意義的迴圈變數名稱
- 適當新增註解說明迴圈的目的
- 保持迴圈主體的簡潔
例外處理:
- 在迴圈中加入適當的例外處理
- 考慮迴圈過程中可能出現的錯誤情況
7. 迴圈的應用場景
- 資料處理:
List<Patient> patients = GetAllPatients();
foreach (Patient patient in patients)
{
UpdatePatientRecord(patient);
}
- 輸入驗證:
string input;
do
{
Console.Write("請輸入有效的體溫數值(35-42):");
input = Console.ReadLine();
} while (!IsValidTemperature(input));
- 定時任務:
while (isNightShift)
{
// 每兩小時查房一次
CheckPatients();
Thread.Sleep(TimeSpan.FromHours(2));
}
透過這些迴圈結構,我們可以更有效率地處理重複性的護理工作,提高工作效率和準確性。在程式設計中,合理使用迴圈結構可以讓我們的程式碼更簡潔、更易維護。
五、程式偵錯
在護理工作中,我們經常需要核對醫囑執行情況,檢查護理記錄是否準確。同樣,在程式設計中,我們也需要檢查程式是否按照預期執行。程式偵錯就是這樣一個核查和糾錯的過程。
1. 偵錯方法
F11 逐陳述式偵錯(單步偵錯)
- 逐行執行程式碼,詳細查看每一步的執行情況
- 就像我們一步步核對醫囑執行過程
- 適合定位具體問題所在的程式碼行
F10 逐程序偵錯
- 以程序為單位執行程式碼,跳過函式內部的詳細執行過程
- 類似於查房時關注重點項目,暫時略過次要細節
- 適合快速了解程式整體執行流程
中斷點偵錯
- 在關鍵程式碼行設定中斷點,程式執行到中斷點處會暫停
- 就像在交接班前重點檢查某些特殊病人的情況
- 方便查看特定位置的變數值和程式狀態
2. 偵錯範例
public class PatientMonitor
{
public void MonitorVitalSigns(Patient patient)
{
// 設定中斷點,檢查病人基本資訊
var temperature = MeasureTemperature(patient);
if (temperature > 37.3)
{
// 使用F11可以進入函式內部查看測量過程
// 使用F10可以跳過處理函式的內部細節
HandleFever(patient, temperature);
}
// 繼續監測其他生命徵象
CheckBloodPressure(patient);
CheckHeartRate(patient);
}
}
3. 偵錯技巧
合理設定中斷點
- 在可能出問題的程式碼處設定中斷點
- 在關鍵業務邏輯的起始點設定中斷點
- 在例外處理程式碼處設定中斷點
使用監看視窗
- 新增關鍵變數到監看視窗
- 即時觀察變數值的變化
- 驗證資料處理的正確性
條件中斷點
- 設定只在特定條件下觸發的中斷點
- 例如,只在體溫超過 39 度時暫停程式
總結
在這一課中,我們學習了以下重要內容:
例外處理
- try-catch 結構的使用
- 不同型別例外的處理方法
- 自訂例外的建立和使用
變數範圍
- 區域變數、類別層級變數和靜態變數的區別
- 變數遮蔽現象
- 範圍的最佳實務
分支結構
- switch-case 陳述式的基本用法
- 支援的資料型別和模式比對
- 與 if-else 結構的對比和選擇
迴圈結構
- while、do-while、for、foreach 的使用場景
- 迴圈控制陳述式(break、continue)
- 迴圈的最佳實務和效能考量
程式偵錯
- 單步偵錯和逐程序偵錯
- 中斷點的設定和使用
- 偵錯工具的有效利用
身為一名從護理轉職到程式開發的學習者,我發現程式設計概念與護理工作有許多相似之處。就像護理工作需要嚴謹的操作規程、清晰的記錄和及時的例外處理一樣,程式設計也需要規範的程式碼結構、清晰的邏輯和完善的錯誤處理。
透過這些基礎知識的學習,我逐漸建立起了程式設計思維,能夠更好地理解和解決程式設計問題。在接下來的學習中,我將繼續深入探索 C# 程式設計的更多特性,為成為一名優秀的開發者打下堅實的基礎。