Skip to content

付録I オブジェクト指向(後半)課題集 — 自習日用

この付録は、第 12 章〜第 15 章(オブジェクト指向の後半) を一通り終えた後の 自習日(課題日) に取り組むための課題集です。

第 12 章で学んだ List<T> と LINQ、第 13 章の継承・ポリモーフィズム、第 14 章のインターフェイス、第 15 章の例外処理を、社員系以外の題材で組み合わせて定着させます。

自習とはいえ講師は常駐しているので、詰まったら質問してください。提出は 任意 ですが、レベル 1・レベル 2 は全員に取り組んでもらうことを推奨します。

観点本編(各章末)付録 H(前半)本付録 I(後半)付録 F(発展)
対象範囲その章の内容第 7〜11 章の定着第 12〜15 章の定着第 0〜31 章を走り切った人向け
提出必須(タイマー方式)任意任意任意
完全なコード本文にありなしなし(仕様・実行例・ヒントのみ)なし
題材社員系が中心業務寄り + 身近図書・図形・楽器・動物・銀行 など業務系
難度章ごとに段階的段階的(3 レベル)段階的(3 レベル)上級寄り

「同じ仕組み(継承・インターフェイス・LINQ・例外処理)を 違う題材 で使う」体験が、この付録のいちばんの狙いです。


本付録には完全なコードを載せません。掲載しているのは:

  • 仕様(何を作るか)
  • 実行結果例(出力イメージ)
  • ヒント(詰まりやすい箇所への指針)

だけです。本編のように「コピペで動く」状態にはしていません。自分で組み立てる練習 のための場だと割り切ってください。

レベル目安時間取り組み方
レベル 1(I-3-1〜I-3-3)各 30〜45 分全員推奨。第 12〜14 章の典型例を別題材で素振り
レベル 2(I-4-1, I-4-2)各 60〜90 分時間が許せば全員。例外処理と複数章の組み合わせ
レベル 3(I-5-1)2〜3 時間以上挑戦したい人。ミニアプリ規模。完成しなくても OK、設計の経験を積む

1 日(7 時間)の自習日なら、レベル 1 + レベル 2 で一通り が標準ペース。レベル 3 は時間が余った人や、もう一段挑戦したい人向けです。

本編と同じく、課題ごとに別プロジェクト を作成してください。 本付録用のソリューション名は KadaiI で、各課題のプロジェクト名は次のとおりです。

課題プロジェクト名
I-3-1 書籍リストの集計KdI_3_1_BookStats
I-3-2 図形の面積と周長KdI_3_2_Shapes
I-3-3 楽器のインターフェイスKdI_3_3_Instruments
I-4-1 銀行口座の安全な入出金KdI_4_1_BankAccount
I-4-2 動物園シミュレータKdI_4_2_Zoo
I-5-1 図書館貸出管理アプリKdI_5_1_LibraryApp

各プロジェクトの csproj 設定(<Nullable>disable</Nullable> など)は本編と同じです。

詰まったときは、講師に丸投げせず、次の 3 点を整理してから相談してください。

  1. 何をしようとしているか(課題のどの仕様に取り組み中か)
  2. どこまで動いているか(画面ショット or 入力したコード)
  3. 何が起きているか(エラーメッセージ、期待した動きと実際の差)

これは配属後にも効く、現場の質問の型 です。


必要なもの用途
Visual Studio 2022全課題
第 15 章まで履修済み全課題
KadaiI ソリューション(新規)全課題

データベースは使いません(本付録は OOP 後半の「LINQ・継承・インターフェイス・例外処理」の練習に集中)。

作業内容参照
プロジェクト作成時の設定最上位レベルのステートメントを使用しない」にチェック第 1 章 1-1
csproj の編集<Nullable>disable</Nullable> に変更第 1 章 1-1
namespace の書き方ファイルスコープ形式(namespace XXX;)第 7 章 冒頭
クラスファイルの追加プロジェクト右クリック → 追加クラス第 7 章 7-7

クラスファイルを自動生成すると、ブロック形式の namespace プロジェクト名 { ... } で作られます。ファイルスコープ形式に書き換えて ください(例:namespace KdI_3_1_BookStats;)。

自習日なので 厳密なタイマーや必須提出はありません。ただし、終わった分は 任意で講師にレビュー依頼 ができます。次のいずれかで提出してください。

  • Git が使える場合:本編と同じく git addgit commitgit push
    • コミットメッセージ例:AppendixI: I-3-1〜I-4-1完成 / I-4-2は途中
  • Git が使えない場合:KadaiI フォルダをサーバへコピー + 提出メモ.txt

第 12〜14 章の典型例を、社員以外の題材で素振りします。全員に推奨 のレベルです。


I-3-1 書籍リストの集計(目安 30〜45 分)

Section titled “I-3-1 書籍リストの集計(目安 30〜45 分)”

関連章:第 12 章(List<T>・LINQ)、第 10 章(コンストラクター)

図書館の蔵書 8 件を List<Book> で持ち、LINQ で 絞り込み・並び替え・集計 を行ってください。

クラス仕様 Book

  • プロパティ(public { get; set; }):
    • Title(string)
    • Author(string)
    • Category(string)(例:"技術書""小説""歴史")
    • Price(int)
    • PublishedYear(int)
  • コンストラクター:
    • Book(string title, string author, string category, int price, int publishedYear)
  • メソッド:
    • PrintShort():"[技術書] C# 入門 / 山田太郎 / 2020年 / 2800円" の形式で 1 行表示

Main メソッド仕様

  • List<Book> で 8 冊のサンプルを作る(カテゴリは 3〜4 種類混在、年は 1995〜2024 の間で散らす)
  • 次の出力をそれぞれ行う:
    1. 全件表示(PrintShortforeach)
    2. カテゴリ「技術書」の冊数(Count(条件))
    3. 全書籍の平均価格(Average)
    4. 価格降順の Top 3(OrderByDescending + Take(3))
    5. 最も古い本(OrderBy + FirstOrDefault)
    6. 著者「山田太郎」の本一覧(Where)
[技術書] C# 入門 / 山田太郎 / 2020年 / 2800円
[小説] 春の旅 / 佐藤花子 / 2015年 / 1500円
[歴史] 江戸の暮らし / 田中一郎 / 1998年 / 1800円
...
技術書の冊数: 3冊
平均価格: 2050円
価格降順 Top3:
1位: 機械学習入門 (4500円)
2位: 詳解C# (3200円)
3位: C# 入門 (2800円)
最も古い本: 江戸の暮らし (1998年)
著者「山田太郎」の本:
- C# 入門
- LINQ 入門
  • Take(3) は LINQ の便利メソッド。using System.Linq; が必要(暗黙的 using で自動)
  • 平均価格を double で得たいなら books.Average(b => b.Price) の戻り値が double になる(整数で表示したいなら (int) でキャスト、または書式 {value:F0})
  • 「最も古い本」は OrderBy(b => b.PublishedYear).FirstOrDefault() が素直。本が空の可能性があるなら FirstOrDefault で null チェック

I-3-2 図形の面積と周長(目安 30〜45 分)

Section titled “I-3-2 図形の面積と周長(目安 30〜45 分)”

関連章:第 13 章(継承、抽象クラス、virtual/override、ポリモーフィズム)、第 10 章(コンストラクター)

図形を表す 抽象クラス と、3 つの具体クラスを作ります。面積と周長は子クラスで計算します。Shape[] にまとめて ポリモーフィズム を体験してください。

抽象クラス仕様 Shape

  • プロパティ:
    • Name(stringpublic { get; private set; }):図形名
  • コンストラクター:
    • Shape(string name)
  • 抽象メソッド:
    • GetArea()(double を返す)
    • GetPerimeter()(double を返す)
  • 通常メソッド:
    • PrintInfo():"[円] 面積 78.54 / 周長 31.42" のような行を表示(NameGetArea/GetPerimeter を組み合わせる)

子クラス仕様

クラスコンストラクター
CircleCircle(double radius)(name = "円")面積 = π r² / 周長 = 2 π r
RectangleRectangle(double width, double height)(name = "長方形")面積 = 幅 × 高さ / 周長 = 2(幅 + 高さ)
TriangleTriangle(double a, double b, double c)(name = "三角形"、3 辺の長さ)面積はヘロンの公式、周長 = a+b+c

ヘロンの公式:s = (a+b+c) / 2、面積 = Math.Sqrt(s*(s-a)*(s-b)*(s-c))。3-4-5 の三角形なら面積は 6 になります。

Main メソッド仕様

  • Shape[] で 5 つの図形(Circle 2 つ + Rectangle 2 つ + Triangle 1 つなど)を作る
  • foreach で全員の PrintInfo() を呼ぶ
  • 全図形の 面積の合計 を表示
  • 最も面積の大きい図形の Name と面積 を表示
[円] 面積 78.54 / 周長 31.42
[円] 面積 28.27 / 周長 18.85
[長方形] 面積 24.00 / 周長 20.00
[長方形] 面積 60.00 / 周長 32.00
[三角形] 面積 6.00 / 周長 12.00
合計面積: 196.81
最大面積: 円 (78.54)
  • 円周率は Math.PI、平方根は Math.Sqrt
  • 面積・周長は double で計算。表示は $"{value:F2}" で小数点 2 桁固定
  • 子クラスのコンストラクターから親へは : base("円") のように渡す(第 13 章 13-3)
  • 最大を探すのはポリモーフィズム経由で OK。shapes.OrderByDescending(s => s.GetArea()).First() が素直(LINQ で型を Shape のまま扱えること自体がポイント)
  • Shape[] の代わりに List<Shape> でも構いません

I-3-3 楽器のインターフェイス(目安 30〜45 分)

Section titled “I-3-3 楽器のインターフェイス(目安 30〜45 分)”

関連章:第 14 章(インターフェイス、複数インターフェイス、インターフェイス型として扱う)、第 12 章(LINQ)

楽器を表すインターフェイスを 2 つ作り、3 種類の楽器クラスに実装します。List<IPlayable> でまとめて ポリモーフィズムでの演奏 と、「チューニング可能なもの」だけを取り出す 操作を体験してください。

インターフェイス仕様 IPlayable

  • プロパティ:
    • Name(stringget のみ):楽器名
  • メソッド:
    • Play()(string を返す):"ギターを弾いた:ジャラーン" のような演奏結果
    • Stop()(戻り値なし):"演奏停止" を表示

インターフェイス仕様 ITunable

  • メソッド:
    • Tune()(戻り値なし):"ギターをチューニングしました" のような表示

クラス仕様

クラス実装するインターフェイスPlay() の例
GuitarIPlayable, ITunable"ギターを弾いた:ジャラーン"
PianoIPlayable"ピアノを弾いた:ポロロン"
DrumIPlayable, ITunable"ドラムを叩いた:ドンドン"

PianoITunable を実装しません(調律は専門家がやるという設計判断)。 どの楽器に対しても Tune() を呼んでいいわけではない、という インターフェイスの「契約」の感覚 を体験してください。

Main メソッド仕様

  • List<IPlayable> に 4 件(例:Guitar / Piano / Drum / Guitar)を入れる
  • 全員に Play() → 結果を 1 行ずつ表示
  • 全員に Stop() を順次呼ぶ
  • List<IPlayable> の中から ITunable を実装しているものだけ を取り出して Tune() を呼ぶ
[Guitar] ギターを弾いた:ジャラーン
[Piano] ピアノを弾いた:ポロロン
[Drum] ドラムを叩いた:ドンドン
[Guitar] ギターを弾いた:ジャラーン
演奏停止
演奏停止
演奏停止
演奏停止
チューニング:
ギターをチューニングしました
ドラムをチューニングしました
ギターをチューニングしました
  • Name プロパティをインターフェイスで宣言する書き方は第 14 章 14-2 を参照
  • インターフェイスは複数実装できる。クラス宣言で class Guitar : IPlayable, ITunable のようにカンマで並べる(第 14 章 14-5)
  • ある楽器が ITunable も持っているかは if (instrument is ITunable tunable) { tunable.Tune(); } のように is 演算子 + パターンマッチ で判定可能(第 11 章で is null の形は学習済み、ここでは型判定の形に拡張)
  • LINQ で書きたいなら instruments.OfType<ITunable>() で「ITunable を実装しているものだけ」が取り出せる

例外処理を主軸にした堅牢性、そして継承 + インターフェイス + LINQ の組み合わせを扱います。時間が許せば全員 に取り組んでもらいたいレベルです。


I-4-1 銀行口座の安全な入出金(目安 60〜90 分)

Section titled “I-4-1 銀行口座の安全な入出金(目安 60〜90 分)”

関連章:第 15 章(例外処理、throwtry-catch-finallyTryParse、ファイル例外)、第 10 章(カプセル化・private set)

銀行口座を表すクラスを作り、入金・出金・履歴保存ができる CUI アプリにします。残高不足や無効な金額は独自例外で throw入力検証は TryParseファイル保存は try-catch-finally を使って堅牢に書いてください。

独自例外仕様 InsufficientBalanceException

  • Exception を継承
  • コンストラクター:InsufficientBalanceException(string message) : base(message) { }

クラス仕様 BankAccount

  • プロパティ(public { get; private set; }):
    • Owner(string):口座名義人
    • Balance(int):残高
  • 内部状態:
    • List<string> で履歴を保持(例:"2026/05/27 10:00 入金 5000円 残高 15000円")
  • コンストラクター:
    • BankAccount(string owner, int initialBalance):initialBalance が 0 未満なら ArgumentExceptionthrow
  • メソッド:
    • Deposit(int amount):amount が 0 以下なら ArgumentException、OK なら残高加算 + 履歴追加
    • Withdraw(int amount):amount が 0 以下なら ArgumentExceptionamount > Balance なら InsufficientBalanceException、OK なら残高減算 + 履歴追加
    • PrintHistory():履歴を全件表示
    • SaveHistoryToFile(string path):履歴をファイルに保存。IOException 系は呼び出し元に伝える(catch は呼び出し側で行う)

Main メソッド仕様

メニューループ:

=== 銀行口座 ===
1: 預け入れ 2: 引き出し 3: 残高表示 4: 履歴表示 5: 履歴ファイル保存 0: 終了
> _
  • 起動時に口座名義人と初期残高を入力 → BankAccount を生成(初期残高は int.TryParse、コンストラクター内例外は捕まえて再入力)
  • 金額入力は int.TryParse で。失敗したら「数字を入れてください」で再入力
  • Deposit / Withdraw の呼び出しは try-catch で囲み、ArgumentException / InsufficientBalanceException を捕まえてメッセージ表示
  • 履歴ファイル保存は try-catch-finally で囲み、finally で「保存処理を完了しました」を必ず表示
=== 銀行口座 ===
口座名義人: 山田二郎
初期残高: 10000
> 1
預け入れ額: 5000
入金完了 残高: 15000円
> 2
引き出し額: 30000
エラー: 残高不足です(残高: 15000円 / 要求: 30000円)
> 2
引き出し額: -100
エラー: 金額は 1 以上を指定してください
> 5
保存ファイル名 (既定: history.txt): [Enter]
保存処理を完了しました
2件の履歴を history.txt に保存しました
> 0
  • 独自例外はクラスを 1 つ作るだけ:public class InsufficientBalanceException : Exception { public InsufficientBalanceException(string message) : base(message) { } }
  • Balanceprivate set にすると、Main から直接書き換えできない(これがカプセル化の効果。第 10 章 10-5)
  • try-catch で複数の例外型を分けて捕まえる場合、子クラス(具体的な例外)を先に書く(第 15 章 15-3)
  • ファイル保存は File.WriteAllLines(path, history)(List<string> も渡せる)
  • finally例外があってもなくても必ず実行 されるので、「保存処理を完了しました」のような後片付けに向く
  • 履歴行のタイムスタンプは DateTime.Now.ToString("yyyy/MM/dd HH:mm")

I-4-2 動物園シミュレータ(目安 60〜90 分)

Section titled “I-4-2 動物園シミュレータ(目安 60〜90 分)”

関連章:第 13 章(継承、抽象クラス)、第 14 章(インターフェイス)、第 12 章(LINQ)

動物園にいる動物を 抽象クラス + インターフェイス で表現し、List<Animal> で管理して LINQ で集計します。

抽象クラス仕様 Animal

  • プロパティ(public { get; private set; }):
    • Name(string)
    • Age(int)
  • コンストラクター:
    • Animal(string name, int age)
  • 抽象メソッド:
    • Speak()(string を返す):鳴き声を返す
  • 通常メソッド:
    • PrintProfile():"[Dog] ポチ 3歳 ワン!" の形式で表示(クラス名は GetType().Name でも、子クラスごとに別途プロパティを持たせても OK)

インターフェイス仕様 IFeedable

  • プロパティ:
    • FoodCostPerDay(intget のみ):1 日あたりの餌代
  • メソッド:
    • Feed()(戻り値なし):"ポチに餌をあげた" のような表示

子クラス仕様

クラス継承 + 実装Speak()FoodCostPerDay
DogAnimal, IFeedable"ワン!"500 円
CatAnimal, IFeedable"ニャー"400 円
BirdAnimal, IFeedable"ピヨピヨ"200 円
FishAnimal のみ"…"(餌代なし)

FishIFeedable を実装しません(餌のあげ方が違う、という設計判断)。 どの動物に対しても Feed() を呼んでいいわけではない、という インターフェイスの「契約」の感覚 を体験してください。

Main メソッド仕様

  • List<Animal> に 7〜8 件入れる(Dog 2, Cat 2, Bird 2, Fish 1〜2)
  • 次の出力を行う:
    1. 全員の PrintProfile()(Speak() 含む)
    2. IFeedable を実装する動物だけ Feed() を呼ぶ
    3. IFeedableFoodCostPerDay の合計
    4. Age >= 5 の動物の数(LINQ)
    5. 「Bird」だけの一覧(OfType<Bird> または is Bird)
[Dog] ポチ 3歳 ワン!
[Dog] ハチ 7歳 ワン!
[Cat] タマ 5歳 ニャー
[Cat] ミケ 2歳 ニャー
[Bird] チュンチュン 2歳 ピヨピヨ
[Bird] ピーちゃん 4歳 ピヨピヨ
[Fish] キン 1歳 …
餌やり:
ポチに餌をあげた
ハチに餌をあげた
タマに餌をあげた
ミケに餌をあげた
チュンチュンに餌をあげた
ピーちゃんに餌をあげた
1日の餌代合計: 2200円
5歳以上の動物: 2匹
鳥類: チュンチュン, ピーちゃん
  • 抽象クラスは abstract class Animal、抽象メソッドは public abstract string Speak();(第 13 章 13-6)
  • 1 クラスで「継承 + インターフェイス実装」を両方やる書き方は class Dog : Animal, IFeedable(第 14 章 14-5)。継承を先、インターフェイスを後 に書くのが慣例
  • IFeedable だけ取り出すのは LINQ で animals.OfType<IFeedable>() が素直
  • Bird だけ取り出すのは animals.OfType<Bird>()、または animals.Where(a => a is Bird) でも可
  • GetType().Name を使うと、Dog のインスタンスから文字列 "Dog" が得られる(リフレクションの入口だが、ここでは「表示用にクラス名を取る便利機能」と割り切って OK)

ここからは 目的を持った小さなアプリ を作ります。完成しなくても OK、設計の経験を積む ことを目的としてください。 レベル 1・レベル 2 を全部終わってから挑戦するのがおすすめです。


I-5-1 図書館貸出管理アプリ(目安 2〜3 時間以上)

Section titled “I-5-1 図書館貸出管理アプリ(目安 2〜3 時間以上)”

関連章:第 12 章(List<T>・LINQ)、第 14 章(インターフェイス)、第 15 章(例外処理・独自例外)、第 10 章(カプセル化)、第 9 章(File 入出力)

メニュー駆動の CUI アプリで、本の登録・貸出・返却・延滞リスト表示・CSV 保存/読込ができる 簡易図書館システム を作ります。Repository パターン(インターフェイス + 実装)独自例外 を取り入れてください。

本アプリは第 14 章 14-7「Service と Repository のイメージ」の発想を、社員以外の題材で実装する場でもあります。第 27 章以降の Web MVC + DB でも同じ構造が出てきます。

クラス仕様 Book

  • プロパティ:
    • BookId(intget; private set;)
    • Title(stringget; private set;)
    • Author(stringget; private set;)
    • IsLent(boolget; private set;):貸出中か
    • BorrowerName(stringget; private set;):借りている人の名前(未貸出時は null)
    • DueDate(DateTime?get; private set;):返却期限(未貸出時は null)
  • コンストラクター:
    • Book(int bookId, string title, string author)(初期状態は未貸出)
  • メソッド:
    • Lend(string borrowerName, DateTime dueDate):貸出処理。既に貸出中なら BookNotAvailableExceptionthrow
    • ReturnBook():返却処理。貸出されていなければ InvalidOperationExceptionthrow
    • PrintShort():"#1 [貸出中] C# 入門 / 山田太郎 / 借主:佐藤花子 / 期限:2026/06/10" の形式で 1 行表示

独自例外仕様

  • BookNotFoundException : Exception:本が見つからないとき
  • BookNotAvailableException : Exception:貸出中の本を借りようとしたとき

それぞれ Exception を継承して、メッセージを受け取るコンストラクターを 1 つ用意するだけで OK。

インターフェイス仕様 IBookRepository

  • メソッド:
    • Add(Book book):1 件追加
    • GetAll():List<Book> を返す
    • FindById(int bookId):見つからなければ BookNotFoundExceptionthrow
    • Lend(int bookId, string borrowerName, int loanDays):本を FindById で取得して Book.Lend を呼ぶ
    • ReturnBook(int bookId):返却
    • GetOverdue(DateTime today):期限切れの貸出中の本一覧
    • SaveToCsv(string path) / LoadFromCsv(string path)

クラス仕様 InMemoryBookRepository : IBookRepository

  • List<Book> を内部に持って各メソッドを実装
  • Main からは IBookRepository repo = new InMemoryBookRepository(); と受けると、後で実装を差し替えやすい構造になる

Main メソッド仕様

メニューループ:

=== 図書館貸出管理 ===
1: 本を追加 2: 全件表示 3: 貸出 4: 返却 5: 延滞リスト
6: CSV保存 7: CSV読込 0: 終了
> _

操作中の各種例外は try-catch で捕まえてメッセージ表示、メニューを抜けないこと。 CSV 入出力は try-catch-finally で。

=== 図書館貸出管理 ===
> 1
タイトル: C# 入門
著者: 山田太郎
追加しました: #1 C# 入門
> 1
タイトル: LINQ 入門
著者: 山田太郎
追加しました: #2 LINQ 入門
> 3
本ID: 1
借主: 佐藤花子
貸出日数 (既定 14): [Enter]
貸出完了: #1 C# 入門 / 借主:佐藤花子 / 期限:2026/06/10
> 3
本ID: 1
借主: 田中一郎
貸出日数: 14
エラー: その本は既に貸出中です(#1 C# 入門)
> 3
本ID: 99
借主: 田中一郎
貸出日数: 14
エラー: ID 99 の本は見つかりません
> 5
延滞中の本(0件):なし
> 6
保存先 (既定: books.csv): [Enter]
保存処理を完了しました
2件を books.csv に保存しました
> 0
  • BookId は手で渡してもいいし、Repository 側で自動採番してもよい(設計次第。_nextId++ で十分)
  • IBookRepository を切ることで、後で SqlBookRepository のような実装を追加する余地ができる(第 27〜30 章で扱う Repository パターンと同じ発想)
  • 各独自例外は 小さなクラス 2 つ で済む。Exception を継承し、コンストラクター 1 本だけでよい
  • CSV の 1 行は bookId,title,author,isLent,borrowerName,dueDate のような並びで OK。null の代わりに空文字列を使うと簡単
  • 入力検証は int.TryParse / DateTime.TryParse、未入力 Enter は既定値で進める
  • GetOverdue(DateTime today)books.Where(b => b.IsLent && b.DueDate.HasValue && b.DueDate.Value < today) のような LINQ
  • メニューループは while (true) { ... } + case "0": return; のような形
  • 貸出履歴(LoanRecord クラス + List<LoanRecord>)を残し、「誰がこれまでに何冊借りたか」を集計
  • 1 人が同時に借りられる上限(3 冊など)を設けて、超過時は例外を投げる
  • CSV だけでなく JSON でも保存できるよう、IBookSerializer インターフェイスを追加して CsvSerializer を実装する(JSON は標準ライブラリ範囲で実装するか、実装は CSV 1 種類のままで「設計だけ」考えてもよい)

時間が残ったら、書いたコードを次の観点で見直してください。

観点確認内容
クラスの責任RepositoryBook の責務が分かれているか。1 クラスがあれもこれもしていないか
継承 vs インターフェイス「is-a」なら継承、「機能の約束」ならインターフェイスを選んでいるか(第 14 章 14-6)
抽象化の階層抽象クラスのメソッドのうち、共通実装が書けるものは virtual、書けないものは abstract にしているか
例外処理想定外を throw、想定内の入力検証は ifTryParse で返しているか
LINQ の使いどきforeach でループしながらフラグを立てるより、Where / Count / Sum の方が短く書けないか
マジックナンバー貸出日数の 14 のような数字に名前(定数 or プロパティ)を付けているか

完璧を目指す必要はありません。「書いた後に自分で読み直して、別の書き方も考えてみる」 習慣そのものが、配属後に効きます。


  • レベル 1(I-3-1〜I-3-3):第 12〜14 章の典型例を 別題材で素振り(LINQ / 継承+ポリモーフィズム / インターフェイス)
  • レベル 2(I-4-1, I-4-2):例外処理を主軸にした 堅牢性、継承 + インターフェイス + LINQ の 組み合わせ
  • レベル 3(I-5-1):Repository パターン + 独自例外 + LINQ + CSV を備えたミニアプリ
  • 完成しなくても OK。書いて、読み直して、質問する サイクルを回すのがいちばんの収穫

次は第 16 章「SQLServer 環境構築」へと進みます。ここまで身につけた List<T> / LINQ / インターフェイス / 例外処理は、DB 接続後も Repository クラスや結果のフィルタ・集計でそのまま使い続けます