(5)從護士到C#開發者-C#基礎進階:例外處理與程式控制

(5)從護士到C#開發者-C#基礎進階:例外處理與程式控制

在C#程式設計學習的第五天,我學習了例外處理、變數作用域、switch-case 陳述式和迴圈結構等內容。作為一名護士轉行開發者,我嘗試將這些程式設計概念與護理工作經驗相結合。

最後更新 2025/3/6 下午8:43
勇敢的天使
預計閱讀 16 分鐘
分類
分享 課程
專題
從護士到C#開發者
標籤
.NET C# 轉行開發 程式設計 迴圈結構

在上一節課中,我學習了型別轉換、算術運算子、關係運算子、邏輯運算子以及各種分支結構,瞭解了這些語法的含義和執行過程。今天的課程聚焦於如何讓我們編寫的程式更具穩固性,減少出錯的可能性。我學習了另一種分支結構 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. 最佳實務

  1. 只捕獲預期的例外,避免捕獲所有例外
  2. 在適當的層級處理例外
  3. 記錄例外資訊以便後續分析
  4. 使用 finally 區塊進行清理工作
  5. 提供有意義的錯誤資訊

二、變數的範圍

變數的範圍指的是能夠使用該變數的範圍。就像在醫院裡,不同科別的護士只能查看自己科別的病人資訊一樣,變數也有其使用範圍的限制。

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. 範圍的最佳實務

  1. 變數的範圍應該儘可能小,這樣可以減少出錯的可能
  2. 避免使用全域變數,因為它們可能被任何程式碼修改
  3. 使用有意義的變數名稱,反映其用途
  4. 及時釋放不再使用的資源
  5. 注意變數的生命週期,避免存取已經超出範圍的變數

三、switch - case 陳述式

在護理工作中,我們經常需要根據不同的情況作出不同的處理決定。switch - case 陳述式就像我們在護理工作中使用的標準處理流程,根據不同的情況選擇相應的處理方案。

1. 支援的型別

switch 陳述式支援以下型別的表達式:

  • 整數型別(intlongbyte等)
  • 字元型別(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. 注意事項

  1. break 陳述式的重要性:每個 case 分支都必須以 break 結束,否則會繼續執行下一個 case
  2. case 的合併:多個 case 可以共用同一個處理邏輯,如範例中的疼痛等級分組
  3. default 分支:用於處理所有未明確指定的情況,類似於護理中的緊急應變措施

5. 分支結構的對比

if、if-else 和 switch 的區別

  1. 條件型別

    • if:可以判斷任何回傳布林值的條件
    • if-else:同樣可以判斷任何布林條件,但提供了替代方案
    • switch:只能判斷相等性,且要求使用常數表達式
  2. 適用場景

    • if:適合單一條件判斷
    • if-else:適合兩種及以上情況的判斷
    • switch:適合多個等值條件的判斷
  3. 效能考量

    • 當分支較少時,三者效能相近
    • 當分支較多時,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;
}

主要區別:

  1. 語法結構

    • if-else if 結構更靈活,可以處理複雜的條件判斷
    • switch-case 結構更規範,程式碼更整潔
  2. 條件限制

    • if-else if 可以使用任何條件表達式
    • switch-case 只能使用相等性比較
  3. 執行流程

    • if-else if 會逐個判斷條件
    • switch-case 直接跳轉到匹配的 case
  4. 程式碼維護

    • 當分支較多時,switch-case 的可讀性和可維護性通常更好
    • if-else if 適合處理複雜的邏輯判斷
  5. 效能考量

    • 對於大量分支的情況,switch-case 通常效能更好
    • 對於少量分支,兩者效能差異不大

6. 使用建議

  1. 當需要根據一個變數的不同值執行不同操作時,優先使用 switch-case
  2. 當分支較多時,switch-case 的可讀性通常優於 if-else
  3. 對於複雜的條件判斷(如範圍判斷),使用 if-else 更合適
  4. 確保所有可能的情況都有相應的處理邏輯

四、迴圈結構

在護理工作中,我們經常需要重複執行某些操作,比如每小時測量一次生命徵象,或者給病房的每個病人查房。在程式設計中,迴圈結構就是用來處理這種重複性工作的。

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. 迴圈控制陳述式

  1. break 陳述式:立即退出迴圈
while (true)
{
    double temperature = MeasureTemperature();
    if (temperature <= 37.3)
    {
        Console.WriteLine("體溫正常,停止監測");
        break; // 體溫正常時退出迴圈
    }
    // 繼續監測
}
  1. continue 陳述式:跳過目前迴圈的剩餘部分,繼續下一次迴圈
for (int bedNumber = 1; bedNumber <= 6; bedNumber++)
{
    if (IsBedEmpty(bedNumber))
    {
        continue; // 如果床位空著,跳過目前迴圈
    }
    // 對有病人的床位進行護理操作
    ProvideNursing(bedNumber);
}

6. 迴圈的最佳實務

  1. 選擇合適的迴圈型別

    • 不確定迴圈次數時使用 while
    • 至少需要執行一次時使用 do-while
    • 知道具體迴圈次數時使用 for
    • 走訪集合時使用 foreach
  2. 避免無限迴圈

    • 確保迴圈條件最終會變為 false
    • 在適當的時候使用 break 陳述式退出迴圈
  3. 效能考量

    • 避免在迴圈中進行不必要的計算
    • 儘可能減少迴圈巢狀的層數
    • 合理使用 continue 陳述式跳過不需要的操作
  4. 程式碼可讀性

    • 使用有意義的迴圈變數名稱
    • 適當新增註解說明迴圈的目的
    • 保持迴圈主體的簡潔
  5. 例外處理

    • 在迴圈中加入適當的例外處理
    • 考慮迴圈過程中可能出現的錯誤情況

7. 迴圈的應用場景

  1. 資料處理
List<Patient> patients = GetAllPatients();
foreach (Patient patient in patients)
{
    UpdatePatientRecord(patient);
}
  1. 輸入驗證
string input;
do
{
    Console.Write("請輸入有效的體溫數值(35-42):");
    input = Console.ReadLine();
} while (!IsValidTemperature(input));
  1. 定時任務
while (isNightShift)
{
    // 每兩小時查房一次
    CheckPatients();
    Thread.Sleep(TimeSpan.FromHours(2));
}

透過這些迴圈結構,我們可以更有效率地處理重複性的護理工作,提高工作效率和準確性。在程式設計中,合理使用迴圈結構可以讓我們的程式碼更簡潔、更易維護。

五、程式偵錯

在護理工作中,我們經常需要核對醫囑執行情況,檢查護理記錄是否準確。同樣,在程式設計中,我們也需要檢查程式是否按照預期執行。程式偵錯就是這樣一個核查和糾錯的過程。

1. 偵錯方法

  1. F11 逐陳述式偵錯(單步偵錯)

    • 逐行執行程式碼,詳細查看每一步的執行情況
    • 就像我們一步步核對醫囑執行過程
    • 適合定位具體問題所在的程式碼行
  2. F10 逐程序偵錯

    • 以程序為單位執行程式碼,跳過函式內部的詳細執行過程
    • 類似於查房時關注重點項目,暫時略過次要細節
    • 適合快速了解程式整體執行流程
  3. 中斷點偵錯

    • 在關鍵程式碼行設定中斷點,程式執行到中斷點處會暫停
    • 就像在交接班前重點檢查某些特殊病人的情況
    • 方便查看特定位置的變數值和程式狀態

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. 偵錯技巧

  1. 合理設定中斷點

    • 在可能出問題的程式碼處設定中斷點
    • 在關鍵業務邏輯的起始點設定中斷點
    • 在例外處理程式碼處設定中斷點
  2. 使用監看視窗

    • 新增關鍵變數到監看視窗
    • 即時觀察變數值的變化
    • 驗證資料處理的正確性
  3. 條件中斷點

    • 設定只在特定條件下觸發的中斷點
    • 例如,只在體溫超過 39 度時暫停程式

總結

在這一課中,我們學習了以下重要內容:

  1. 例外處理

    • try-catch 結構的使用
    • 不同型別例外的處理方法
    • 自訂例外的建立和使用
  2. 變數範圍

    • 區域變數、類別層級變數和靜態變數的區別
    • 變數遮蔽現象
    • 範圍的最佳實務
  3. 分支結構

    • switch-case 陳述式的基本用法
    • 支援的資料型別和模式比對
    • 與 if-else 結構的對比和選擇
  4. 迴圈結構

    • while、do-while、for、foreach 的使用場景
    • 迴圈控制陳述式(break、continue)
    • 迴圈的最佳實務和效能考量
  5. 程式偵錯

    • 單步偵錯和逐程序偵錯
    • 中斷點的設定和使用
    • 偵錯工具的有效利用

身為一名從護理轉職到程式開發的學習者,我發現程式設計概念與護理工作有許多相似之處。就像護理工作需要嚴謹的操作規程、清晰的記錄和及時的例外處理一樣,程式設計也需要規範的程式碼結構、清晰的邏輯和完善的錯誤處理。

透過這些基礎知識的學習,我逐漸建立起了程式設計思維,能夠更好地理解和解決程式設計問題。在接下來的學習中,我將繼續深入探索 C# 程式設計的更多特性,為成為一名優秀的開發者打下堅實的基礎。

繼續探索

延伸閱讀

更多文章
同分類 / 同標籤 2025/8/13

推薦一款高效能狀態機管理解決方案

在實際軟體開發中,尤其是工業軟體,每一款設備都有複雜的狀態以及狀態之間的切換的功能需求,在這種情況下,如何管理狀態以及狀態之間切換,和對應狀態下的功能控制,成為非常重要的一個問題。

繼續閱讀
同分類 / 同標籤 2025/3/18

(7)從護士到C#開發者--物件導向程式設計基礎

作為一名從護理行業轉行的程式設計師,我將分享如何透過醫護工作經驗來理解物件導向程式設計的概念。本文將介紹類別、物件、屬性、方法等物件導向的核心概念,並結合醫療保健領域的實例來加深理解。

繼續閱讀