1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C# LINQ のラムダ変数を日本語にすると業務クエリの可読性が上がる

1
Posted at

TL;DR

  • LINQ のラムダ変数・クエリ変数を日本語にするとビジネスルールがそのまま読める
  • クエリ構文(from 社員 in 社員一覧 where ...)は仕様書の条件式に近い構造になる
  • Join など複数コレクションが絡む処理で特に可読性向上の恩恵が大きい
  • GroupJoinjoin ... into でクエリ構文でも書ける
  • AggregateSkipTakeDistinct はクエリ構文に対応キーワードがなく、メソッド構文のみ

環境

項目 バージョン・詳細
OS Windows 11
Visual Studio 2022 (17.x)
.NET .NET 8
対象 社内業務アプリ(WinForms)

英語変数名 vs 日本語変数名の比較

メソッド構文

// 英語変数名
var result = employees
    .Where(e => e.Department == "営業" && e.IsActive)
    .OrderBy(e => e.HireDate)
    .Select(e => new { e.Name, e.HireDate });

// 日本語変数名
var 結果 = 社員一覧
    .Where(社員 => 社員.部署 == "営業" && 社員.在籍中)
    .OrderBy(社員 => 社員.入社日)
    .Select(社員 => new { 社員.氏名, 社員.入社日 });

e が何を指すか文脈を覚えておく必要がなくなる。チェーンを上から読み下すだけで何をしているかわかる。

クエリ構文

var 結果 = from 社員 in 社員一覧
           where 社員.部署 == "営業" && 社員.在籍中
           orderby 社員.入社日
           select new { 社員.氏名, 社員.入社日 };

from 社員 in 社員一覧 の構造は仕様書の「社員一覧の中で…」に直接対応する。Excel仕様書と並べて確認する作業がスムーズになる。

集計・グループ化

// 部署ごとの残業時間合計(降順)
var 部署別残業集計 = 勤怠一覧
    .GroupBy(勤怠 => 勤怠.部署)
    .Select(グル => new
    {
        部署名 = グル.Key,
        残業時間合計 = グル.Sum(勤怠 => 勤怠.残業時間),
        対象人数 = グル.Count()
    })
    .OrderByDescending(集計 => 集計.残業時間合計);

ラムダ変数が 勤怠グループ集計 と変化するたびに「今何を操作しているか」が明示される。英語だと gx のような短縮変数になりがちで、ネストが深くなると追いにくい。

Join

// 英語
var result = orders.Join(customers,
    o => o.CustomerId,
    c => c.Id,
    (o, c) => new { o.OrderDate, c.Name, o.Amount });

// 日本語
var 注文明細 = 注文一覧.Join(顧客一覧,
    注文 => 注文.顧客ID,
    顧客 => 顧客.ID,
    (注文, 顧客) => new { 注文.注文日, 顧客.氏名, 注文.金額 });

引数が4つある Join は英語1文字変数だと特に読みにくい。注文顧客 という名前があるだけで、各引数が何のキーを結合しているか一目でわかる。

GroupJoin はクエリ構文でも書ける

// クエリ構文での GroupJoin(join ... into)
var 結果 = from 部署 in 部署一覧
           join 社員 in 社員一覧
               on 部署.ID equals 社員.部署ID
               into 部署社員グル
           select new
           {
               部署名 = 部署.名称,
               人数 = 部署社員グル.Count()
           };

join ... into 構文を使えば GroupJoin をクエリ構文で表現できる。左外部結合は into の後に追加の from 句と DefaultIfEmpty() を組み合わせて書く。

// 左外部結合(部署に社員がいない場合も部署を含める)
var 結果 = from 部署 in 部署一覧
           join 社員 in 社員一覧
               on 部署.ID equals 社員.部署ID
               into 部署社員グル
           from 社員 in 部署社員グル.DefaultIfEmpty()  // この from 句が左外部結合のポイント
           select new
           {
               部署名 = 部署.名称,
               氏名 = 社員?.氏名 ?? "(未所属)"
           };

クエリ構文で書けない操作

以下はクエリ構文に対応するキーワードがなく、メソッド構文でしか書けない。

操作 メソッド構文
集計 Aggregate
ページング Skip / Take
重複除去 Distinct
全件一致・任意一致 All / Any
先頭・末尾 First / Last / Single

実務では「フィルタリング・ソートはクエリ構文、ページングや集計はメソッド構文」という混在スタイルが多くなる。

// クエリ構文でフィルタ → メソッド構文でページング
var ジ結果 = (from 社員 in 社員一覧
                 where 社員.在籍中
                 orderby 社員.入社日
                 select 社員)
                .Skip(ジ番号 * ジサイズ)
                .Take(ジサイズ)
                .ToList();

ハマりどころ

IME の干渉

=> を打った直後に IME が自動でオンになることがある。ラムダ変数の入力タイミングで IME を手動制御する習慣をつけると解消する。Visual Studio の「ツール > オプション > テキストエディター > C# > IntelliSense」で補完タイミングの調整も有効。

行長の増加

日本語変数名は英語1文字変数より横幅をとる。チェーンが長い場合は各メソッドを改行して縦展開する。

// 縦展開の例
var 対象 = 社員一覧
    .Where(社員 => 社員.在籍中)
    .Where(社員 => 社員.入社日 < 基準日)
    .OrderBy(社員 => 社員.氏名)
    .Select(社員 => 社員.氏名)
    .ToList();

汎用ユーティリティは英語に戻す

特定ドメインに紐づくクエリは日本語変数が有効だが、複数箇所から呼ばれる汎用フィルタ・拡張メソッドを切り出す場合は英語に戻すほうが無難。日本語変数の効果はドメイン文脈が明確な場所ほど高い。

まとめ

  • LINQ ラムダ変数を日本語にすると「何のコレクションの何を操作しているか」がコードから直接読める
  • クエリ構文と日本語変数の組み合わせは仕様書の条件式に近い構造になり、照合コストが下がる
  • GroupJoin はクエリ構文の join ... into で書ける
  • AggregateSkipTakeDistinct 等はメソッド構文のみ。混在スタイルが実用的
  • IME干渉と行長には慣れが必要。縦展開を意識すると解消しやすい

概要や実際の開発体験を含めた記事はこちら → C# の LINQ を日本語変数で書いたら、クエリが仕様書みたいになった話

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?