前回の授業では、型変換、算術演算子、関係演算子、論理演算子、そしてさまざまな分岐構造について学び、これらの構文の意味と実行プロセスを理解しました。今日の授業では、プログラムの堅牢性を高め、エラーが発生する可能性を減らす方法に焦点を当てました。別の分岐構造である 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: null オブジェクトにアクセスしようとした場合にスローされます
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("オブジェクトがnullです");
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:2つ以上の条件の判定に適している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;
}
主な違い:
構文構造:
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時間ごとのバイタルサイン測定や各病室の患者回診など、同じ操作を繰り返し実行する必要がよくあります。プログラミングでは、ループ構造を使ってこのような反復作業を処理します。
1. while ループ
while ループは、条件が true の間、コードブロックを繰り返し実行します。看護業務で患者の体温が回復するまで継続的に監視するようなものです。
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)
{
// 2時間ごとに回診する
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# プログラミングのさらなる特性を深く探求し、優れた開発者になるための強固な基盤を築いていきます。