第4章:C#でやりがち!KISSリファクタ🍰🔧
(C#あるあるの“難しく見える書き方”を、自然に読みやすく直すよ〜😊✨)
この章のゴール🎯✨
この章が終わったら、こうなれる!💪💗
- 「うっ…読みにくい…😵」って感じるC#コードを、**“自然に読める形”**に直せる
- switch / LINQ / null / 例外 / async の ややこしさの正体が分かって、整えられる
- AI(Copilot / Codex系)にお願いしても、余計に壮大にされずにKISSで直せる🤖🫶
ちなみに今どきのC#は、.NET 10(LTS)+C# 14が軸だよ〜くらいの感覚でOK🙆♀️✨ (Microsoft)
4-0 まずは「KISSリファクタの手順」を覚えよう🍰✨(超重要)
リファクタって、センスじゃなくて手順ゲー😺🧠
🍓KISSリファクタ 6手(これだけで回せる)
- 挙動を守る🛡️(先に小さいテスト or 例入力を用意)
- 混ざってる責務を発見👀(入力チェック/業務ルール/表示/保存…が混ざってない?)
- 境界を決める🚪(例外はどこで受ける? nullは入口で止める?)
- 名前で分ける📛(まず Extract Method / 変数名で説明)
- 分岐を整理🎭(switchが肥大→データ化 or 小メソッド化)
- 読みやすさを最優先📖(賢い1行より、分かる3行✨)
4-1 switch/パターンが増えすぎ問題🎭🌀

switch式やパターンマッチは便利!でも、“正しさ”と引き換えに読みにくくなりやすい😇
😵あるある:switchが「仕様書」になってしまう
- caseが20個…
- whenがいっぱい…
- 例外も混ざる…
- しかも各caseの中で別の分岐…(地獄2段重ね🍱🔥)
✅KISSに直すコツ(優先順)
(A) 入口でガード節🚪:入力不正は最初に返す (B) caseの中身をメソッド化📛:caseが見出しになる (C) 分岐が“データ”なら辞書化🧺:if/switchをデータに置き換える (D) 行動が“種類ごと”なら小さなStrategy🧩:ただし増やしすぎ注意⚠️
例:switchが重くて読めない → 「見出し化」で一気に楽✨
❌Before(読めない…😵)
public decimal CalcFee(User user, Plan plan)
{
if (user is null) throw new ArgumentNullException(nameof(user));
return plan switch
{
Plan.Free => 0m,
Plan.Student when user.Age < 20 => 100m,
Plan.Student when user.Age >= 20 => 200m,
Plan.Pro when user.IsVip => user.Country switch
{
"JP" => 980m,
"US" => 9.99m,
_ => 1200m
},
Plan.Pro => 1200m,
_ => throw new NotSupportedException()
};
}
✅After(読む順番が自然📖✨)
public decimal CalcFee(User user, Plan plan)
{
ArgumentNullException.ThrowIfNull(user);
return plan switch
{
Plan.Free => 0m,
Plan.Student => CalcStudentFee(user),
Plan.Pro => CalcProFee(user),
_ => throw new NotSupportedException($"Unknown plan: {plan}")
};
}
private static decimal CalcStudentFee(User user)
=> user.Age < 20 ? 100m : 200m;
private static decimal CalcProFee(User user)
{
if (user.IsVip) return CalcVipProFee(user.Country);
return 1200m;
}
private static decimal CalcVipProFee(string country)
=> country switch
{
"JP" => 980m,
"US" => 9.99m,
_ => 1200m
};
✨ポイント:switchを“仕様の見出し”にして、中身は小メソッドに逃がす📛 読む人の脳内が「いま何してる?」で迷子にならないよ〜🫶
4-2 LINQで“賢くしすぎ”問題🧠⚡

LINQは気持ちいい!でも、**長いチェーンは“謎の呪文”**になりがち😂
😵あるある:何をしてるか説明できないLINQ
- Select/Where/GroupByが連結しまくり
- 途中の意図が見えない
- 例外やnullが混ざると最悪🌧️
✅KISSに直すコツ
- 途中変数で“説明”する📛(これだけで8割勝つ😺)
- 「ドメイン語(業務用語)」で命名する🗣️
- 長いクエリは 2〜3段に分ける🍰
❌Before(呪文🙄)
var result = orders
.Where(o => o.Status == OrderStatus.Paid && o.Items.Any())
.SelectMany(o => o.Items.Select(i => new { o.CustomerId, i.Price, i.Qty }))
.GroupBy(x => x.CustomerId)
.Select(g => new { CustomerId = g.Key, Total = g.Sum(x => x.Price * x.Qty) })
.OrderByDescending(x => x.Total)
.Take(10)
.ToList();
✅After(途中変数で“意味”が出る✨)
var paidOrders = orders
.Where(o => o.Status == OrderStatus.Paid && o.Items.Any());
var orderLines = paidOrders
.SelectMany(o => o.Items.Select(i => new OrderLine(o.CustomerId, i.Price, i.Qty)));
var totalsByCustomer = orderLines
.GroupBy(x => x.CustomerId)
.Select(g => new CustomerTotal(g.Key, g.Sum(x => x.Price * x.Qty)));
var top10 = totalsByCustomer
.OrderByDescending(x => x.Total)
.Take(10)
.ToList();
✨「賢い1行」より「分かる3段」🍰💗 これがKISS〜!
4-3 null処理が散る問題🌧️(“入口で止める”が最強)

null対応って、散ると一気に読めなくなる😵
✅KISSの基本:nullは“境界”でまとめる🚪
- 受け取った瞬間に
ThrowIfNull - あるいは「なければ既定値」に寄せる
- 以降の処理は nullがない世界で考える🌈
❌Before(あちこちでnullチェック…😵)
if (user != null && user.Profile != null && user.Profile.Address != null)
{
if (user.Profile.Address.ZipCode != null)
{
// ...
}
}
✅After(入口で確定させる✨)
ArgumentNullException.ThrowIfNull(user);
var address = user.Profile?.Address
?? throw new InvalidOperationException("Address is required.");
var zip = address.ZipCode
?? throw new InvalidOperationException("ZipCode is required.");
// ここから下は「nullが無い前提」でスッキリ書ける✨
4-4 例外が飛び回る問題🧨(境界だけで受ける🧯)
例外は便利だけど、コードのどこでも投げ始めると追跡が地獄😭
✅KISSルール(おすすめ)
- “期待される失敗”は例外にしない(TryParse / Result / bool返す)
- “想定外”だけ例外(壊れた状態は早めに爆発💥)
- catchは境界でまとめる(UI層/API層/ジョブの入口など)
例:期待される失敗 → Try系に寄せる
public bool TryGetDiscountRate(string code, out decimal rate)
{
if (string.IsNullOrWhiteSpace(code))
{
rate = 0m;
return false;
}
// 例:DBや辞書などから取得(ここでは簡略)
if (code == "OFF10")
{
rate = 0.10m;
return true;
}
rate = 0m;
return false;
}
4-5 asyncが絡むと追跡が大変問題⏳(やることを分けよう)
async/await自体は最高✨ でも「全部asyncで一気にやる!」ってすると、責務が混ざって追えない🥲
✅KISSのコツ
- asyncメソッドは“やることを少なく”(1関数1仕事🎯)
- I/O(API/DB)と業務ロジックを分ける(テストしやすい🧪)
- CancellationTokenを入口から渡す(止められる安心感🫶)
✅おすすめ構造(雰囲気)
public async Task<OrderResult> PlaceOrderAsync(PlaceOrderRequest req, CancellationToken ct)
{
Validate(req);
var customer = await _customerRepo.GetAsync(req.CustomerId, ct);
var price = _pricingService.Calculate(req.Items, customer); // ここは同期でOKにするのがKISS✨
await _orderRepo.SaveAsync(customer.Id, req.Items, price, ct);
return new OrderResult(price);
}
4-6 「新しい文法=必ず良い」ではない🍵
C# 14は .NET 10 と一緒に使えるよ〜、という“土台”だけ押さえればOK🙆♀️✨ (Microsoft Learn)
💡使う基準はシンプル:
- 読む人がラクになる?(未来の自分も含む😺)
- デバッグがラクになる?
- チーム/自分のルールと合う?
「新機能だから入れる」は、KISS的には負けやすい🥺
4-7 ミニ課題📝✨(章のゴールに直結!)
テーマ:「注文金額計算」を 混ぜない 形に整理する🎀
🎯やること(3ステップ)
- 入力不正(null/空/範囲)を 入口で止める🚪
- 業務ルール(割引/税)を Calculate系に隔離🍰
- 表示用フォーマット(通貨/小数/文字列)を 最後にまとめる🪄
✅チェックポイント(自己採点✅)
- 1メソッドに「チェック」「計算」「表示」が混ざってない?
- switch/ifが“見出し”として読める?
- nullチェックが散ってない?
- 例外を投げる場所が一貫してる?
4-8 AIに頼むときの“第4章向け”テンプレ🤖💗
第5章ほどガッツリはやらないけど、第4章でもAIは超使えるよ〜🫶
🍓コピペ用プロンプト(選択範囲に対して)
- 「挙動を変えずに読みやすくKISSにリファクタして」
- 「差分は小さく、メソッド分割と命名改善だけで」
- 「switch/LINQ/null/例外/async の 複雑さを減らす方向で」
- 「変更後に、何をどう整理したか3行で説明して」
CopilotはVS側でも統合が進んでて、.NET 10/VS 2026世代ではAI支援がより前提になってるよ〜✨ (Microsoft for Developers)
章末まとめ🍰✅(今日の持ち帰り)
- switchは「見出し」にして、中身を小メソッドへ📛
- LINQは途中変数で“説明”するだけでKISSになる🧁
- null/例外は“境界でまとめる”と急に読みやすい🚪🧯
- asyncは責務分離+入口からCancellationTokenが安心⏳🫶
次は「第4章のミニ課題」に使う “わざと汚い注文計算コード”の練習素材も作れるよ😺🍰 すぐ実践したいなら、あなたの題材(個人プロジェクトでもOK)っぽい例に寄せてサンプルコード出すね✨