第12章 List<T> と LINQ
この章の目的
Section titled “この章の目的”この章では、複数のデータをまとめて扱う List<T> と、データの検索・並び替え・集計を短く書く LINQ を学習します。
この章は 2 部構成 です。
前半:List<T> だけでできること
Section titled “前半:List<T> だけでできること”List<T> は配列の進化版で、要素の追加・削除が自由にできる「データの入れ物」です。
第 6 章の配列には「作成時に要素数が決まる」「後から追加・削除しにくい」という制約がありましたが、List<T> はそれを解消します。
List<int> scores = new List<int>();scores.Add(80);scores.Add(75);そして、List<T> は foreach と組み合わせるだけで、一覧表示・検索・集計(合計・人数・平均)まで、実用的な処理はひととおりこなせます。
LINQ を知らなくても、List<T> は単体で十分に使えます。 まずはこの前半を確実にしましょう。
後半:LINQ とラムダ式でもっと便利に(発展)
Section titled “後半:LINQ とラムダ式でもっと便利に(発展)”前半で foreach を使って書いた検索・並び替え・集計は、LINQ を使うと数行で短く書けます。
List<Employee> salesEmployees = employees .Where(employee => employee.DepartmentName == "営業") .ToList();ただし、LINQ とラムダ式は少し難しい内容です。まずは「読めて、真似て書ける」ところを目標 にしてください。
難しいと感じたら、いったん前半の List + foreach に戻って構いません。ただし LINQ は第 17 章以降の DB・Web 編で当たり前に登場する ため、第 17 章に入るまでに、基本パターンを真似てでも書けるようにしておくことは必須 です(詳しくは後半の冒頭で説明します)。
後の DB 接続編では、データベース(第 17 章以降の SQLServer など)の employees テーブルから取得したデータを List<Employee> として扱い、検索や集計を行います。
この章は、DB 連携・Web アプリ・デスクトップアプリにつながる重要な準備です。
この章でできるようになること
Section titled “この章でできるようになること”この章を終えると、次のことができるようになります。
前半:List<T>(まずはここを確実に)
- 配列と
List<T>の違いを説明できる List<int>・List<string>を作成し、要素の追加・削除ができるCountで件数を取得し、foreachで全要素を処理できる- 自作クラスを
Tに指定したList<Employee>を扱える foreachで検索・集計(合計・人数・平均)ができる
後半:LINQ とラムダ式(基本パターンは第 17 章までに必須・読めて真似て書ければOK)
- LINQ の役割を説明できる
Whereで条件絞り込み、FirstOrDefaultで 1 件取得ができる(結果が null になりうることも意識する)- ラムダ式の基本的な読み方が分かる
OrderBy・OrderByDescendingで並び替えができるSum・Average・Count(条件)で集計できるSelectで必要な項目だけを取り出せる- DB 連携を想定した
List<Employee>の使い方を説明できる
本章で使用する環境
Section titled “本章で使用する環境”| 項目 | 内容 |
|---|---|
| 開発環境 | Visual Studio 2022 |
| プロジェクト種類 | コンソール アプリ |
| 対象フレームワーク | .NET 8 |
| ソリューション名 | Chapter12 |
| プロジェクト名 | Ch12_ListLinq |
csproj の Nullable は disable に変更してください
プロジェクト作成後、
Ch12_ListLinq.csprojを開き、<Nullable>disable</Nullable>に変更してください。詳しい手順は、第 1 章「1-1 プロジェクトを作成する」を参照してください。
作業前チェック
Section titled “作業前チェック”作業を始める前に、次の内容を確認してください。
- 配列と
foreach文を使える - クラス・プロパティ・コンストラクターを書ける
- オブジェクト初期化子を使える
-
nullチェックをif文で書ける(第 11 章) - 自作クラスを別ファイル(
Employee.csなど)に書ける - 第 11 章の内容を Git に提出済みである
〔前半〕List<T> だけでできること
Section titled “〔前半〕List<T> だけでできること”ここから 前半 です。List<T> を foreach と組み合わせて使います。
この前半では LINQ は使いません。 「List だけでも、一覧・検索・集計までできる」ことを体験してください。
12-1 List<T> の基礎
Section titled “12-1 List<T> の基礎”List<T> とは
Section titled “List<T> とは”List<T> は、同じ型のデータを 複数管理 するための仕組み(コレクション)です。
T の部分には、扱いたい型を指定します。
| 書き方 | 意味 |
|---|---|
List<int> | int の値を複数管理する |
List<string> | string の値を複数管理する |
List<Employee> | Employee のオブジェクトを複数管理する |
List<T> は System.Collections.Generic 名前空間にありますが、暗黙的 using で取り込まれているため、using を書く必要はありません(第 9 章参照)。
List<int> を作成する
Section titled “List<int> を作成する”Add メソッドで要素を追加し、foreach で全件処理します。
namespace Ch12_ListLinq;
internal class Program{ static void Main(string[] args) { List<int> scores = new List<int>();
scores.Add(80); scores.Add(75); scores.Add(90);
foreach (int score in scores) { Console.WriteLine(score); } }}実行結果:
807590Count で件数を取得する
Section titled “Count で件数を取得する”List<T> の要素数は Count プロパティ で取得します。
List<int> scores = new List<int> { 80, 75, 90 };
Console.WriteLine($"件数:{scores.Count}");実行結果:
件数:3配列の Length と対応する位置づけです。どちらもプロパティなので () は付けません。
| 種類 | 件数取得 |
|---|---|
配列(int[]) | scores.Length |
List<T>(List<int>) | scores.Count |
初期値を入れて作成する
Section titled “初期値を入れて作成する”new List<T> { ... } の形で、最初から値を入れて作成できます。
List<string> departmentNames = new List<string>{ "総務", "営業", "開発", "マーケティング"};
foreach (string name in departmentNames){ Console.WriteLine(name);}実行結果:
総務営業開発マーケティングインデックスでアクセスする
Section titled “インデックスでアクセスする”List<T> も配列と同じく、0 始まりのインデックス で要素にアクセスできます。
List<string> departmentNames = new List<string> { "総務", "営業", "開発" };
Console.WriteLine(departmentNames[0]); // 総務Console.WriteLine(departmentNames[1]); // 営業Console.WriteLine(departmentNames[2]); // 開発Remove で削除する
Section titled “Remove で削除する”指定した値を削除するには Remove を使います。
List<string> departmentNames = new List<string> { "総務", "営業", "開発" };
departmentNames.Remove("営業");
foreach (string name in departmentNames){ Console.WriteLine(name);}実行結果:
総務開発要素の位置(インデックス)を指定して削除する RemoveAt(1) もあります。
配列と List<T> の違い
Section titled “配列と List<T> の違い”| 項目 | 配列 | List<T> |
|---|---|---|
| 要素数 | 作成時に固定 | 後から増減できる |
| 件数取得 | Length(プロパティ) | Count(プロパティ) |
| 追加 | (専用メソッドなし) | Add(値) |
| 削除 | (専用メソッドなし) | Remove(値)、RemoveAt(インデックス) |
| 向く場面 | 件数が決まっているデータ | 件数が変わるデータ |
後の DB 接続編では、SELECT 文の結果が何件返ってくるか実行前には分かりません。
そのため、DB から取得したデータは List<T> で扱う のが自然です。
12-2 List<Employee> でオブジェクトを管理する
Section titled “12-2 List<Employee> でオブジェクトを管理する”自作クラスのリスト
Section titled “自作クラスのリスト”List<T> の T には、自分で作ったクラスも指定できます。
ここでは、社員情報を表す Employee クラスのリストを作ります。
Employee.cs:
namespace Ch12_ListLinq;
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } public string DepartmentName { get; set; } public int Salary { get; set; }}Program.cs:
namespace Ch12_ListLinq;
internal class Program{ static void Main(string[] args) { List<Employee> employees = new List<Employee>();
employees.Add(new Employee { EmployeeId = 1001, EmployeeName = "山田二郎", DepartmentName = "総務", Salary = 500000 });
employees.Add(new Employee { EmployeeId = 1002, EmployeeName = "佐藤昭夫", DepartmentName = "営業", Salary = 500000 });
employees.Add(new Employee { EmployeeId = 1003, EmployeeName = "山口洋子", DepartmentName = "開発", Salary = 500000 });
foreach (Employee employee in employees) { Console.WriteLine($"{employee.EmployeeId}:{employee.EmployeeName}({employee.DepartmentName})"); } }}実行結果:
1001:山田二郎(総務)1002:佐藤昭夫(営業)1003:山口洋子(開発)サンプルデータ作成メソッドを別クラスに分ける
Section titled “サンプルデータ作成メソッドを別クラスに分ける”毎回 Main の中にサンプルデータを書くと長くなります。
サンプルデータを返す静的メソッド を別クラスに分けておくと、Main がすっきりします。
EmployeeSampleData.cs:
namespace Ch12_ListLinq;
static class EmployeeSampleData{ public static List<Employee> Create() { return new List<Employee> { new Employee { EmployeeId = 1001, EmployeeName = "山田二郎", DepartmentName = "総務", Salary = 500000 }, new Employee { EmployeeId = 1002, EmployeeName = "佐藤昭夫", DepartmentName = "営業", Salary = 500000 }, new Employee { EmployeeId = 1003, EmployeeName = "山口洋子", DepartmentName = "開発", Salary = 500000 }, new Employee { EmployeeId = 1006, EmployeeName = "佐々木明子", DepartmentName = "総務", Salary = 800000 }, new Employee { EmployeeId = 1009, EmployeeName = "星野健一", DepartmentName = "営業", Salary = 400000 } }; }}Program.cs:
namespace Ch12_ListLinq;
internal class Program{ static void Main(string[] args) { List<Employee> employees = EmployeeSampleData.Create();
foreach (Employee employee in employees) { Console.WriteLine($"{employee.EmployeeId}:{employee.EmployeeName}({employee.DepartmentName})"); } }}実行結果:
1001:山田二郎(総務)1002:佐藤昭夫(営業)1003:山口洋子(開発)1006:佐々木明子(総務)1009:星野健一(営業)以降のサンプルでも、この EmployeeSampleData.Create() で作った List<Employee> を使い回します。
foreach で愚直に検索してみる
Section titled “foreach で愚直に検索してみる”LINQ を学ぶ前に、まずは foreach で社員 1002 を検索してみます。
namespace Ch12_ListLinq;
internal class Program{ static void Main(string[] args) { List<Employee> employees = EmployeeSampleData.Create();
Employee foundEmployee = null;
foreach (Employee employee in employees) { if (employee.EmployeeId == 1002) { foundEmployee = employee; break; } }
if (foundEmployee == null) { Console.WriteLine("社員が見つかりませんでした。"); } else { Console.WriteLine($"{foundEmployee.EmployeeId}:{foundEmployee.EmployeeName}"); } }}実行結果:
1002:佐藤昭夫このように、foreach と if を組み合わせれば検索できます。
foreach で集計してみる(合計・人数・平均)
Section titled “foreach で集計してみる(合計・人数・平均)”検索だけでなく、合計・人数・平均 のような集計も foreach だけで書けます。
namespace Ch12_ListLinq;
internal class Program{ static void Main(string[] args) { List<Employee> employees = EmployeeSampleData.Create();
int totalSalary = 0; int salesCount = 0;
foreach (Employee employee in employees) { totalSalary += employee.Salary; // 給与を足していく
if (employee.DepartmentName == "営業") { salesCount++; // 営業の人数を数える } }
int averageSalary = totalSalary / employees.Count;
Console.WriteLine($"給与合計:{totalSalary}円"); Console.WriteLine($"平均給与:{averageSalary}円"); Console.WriteLine($"営業の人数:{salesCount}人"); }}実行結果:
給与合計:2700000円平均給与:540000円営業の人数:2人合計は「変数に足し込む」、件数は「条件に合うたびにカウントを増やす」、平均は「合計 ÷ 件数」。
List と foreach だけで、検索も集計もできる ことが分かります。
最後に、ここまでの道具(List・Add・Remove・foreach・while・if)を組み合わせて、実際に操作できる小さなプログラム を作ってみましょう。
List で動くミニプログラム(一覧・追加・削除)
Section titled “List で動くミニプログラム(一覧・追加・削除)”メニューを選んで、品物を 一覧表示・追加・削除 できる「買い物メモ」プログラムを作ります。
ここでも LINQ は使いません。 これまでに学んだ List・foreach・while・if の組み合わせだけで、立派に動くプログラムになります。
namespace Ch12_ListLinq;
internal class Program{ static void Main(string[] args) { List<string> items = new List<string>();
while (true) { Console.WriteLine(); Console.WriteLine("=== 買い物メモ ==="); Console.WriteLine("1: 一覧表示 2: 追加 3: 削除 0: 終了"); Console.Write("> "); string input = Console.ReadLine();
if (input == "1") { if (items.Count == 0) { Console.WriteLine("(メモは空です)"); } else { foreach (string item in items) { Console.WriteLine($"- {item}"); } } } else if (input == "2") { Console.Write("追加する品物:"); string name = Console.ReadLine(); items.Add(name); Console.WriteLine($"「{name}」を追加しました。"); } else if (input == "3") { Console.Write("削除する品物:"); string name = Console.ReadLine();
if (items.Remove(name)) { Console.WriteLine($"「{name}」を削除しました。"); } else { Console.WriteLine($"「{name}」は見つかりませんでした。"); } } else if (input == "0") { Console.WriteLine("終了します。"); break; } else { Console.WriteLine("0〜3 を入力してください。"); } } }}実行結果例(入力した部分は太字相当):
=== 買い物メモ ===1: 一覧表示 2: 追加 3: 削除 0: 終了> 2追加する品物:りんご「りんご」を追加しました。
=== 買い物メモ ===1: 一覧表示 2: 追加 3: 削除 0: 終了> 2追加する品物:牛乳「牛乳」を追加しました。
=== 買い物メモ ===1: 一覧表示 2: 追加 3: 削除 0: 終了> 1- りんご- 牛乳
=== 買い物メモ ===1: 一覧表示 2: 追加 3: 削除 0: 終了> 3削除する品物:りんご「りんご」を削除しました。
=== 買い物メモ ===1: 一覧表示 2: 追加 3: 削除 0: 終了> 0終了します。仕組みを分解すると、すべて既習の組み合わせです。
| 部分 | 使っているもの | 章 |
|---|---|---|
| メニューを繰り返す | while (true) と break(0 で抜ける) | 第 5 章 |
| 選択肢の判定 | if / else if と文字列の比較(input == "1") | 第 4 章 |
| 一覧表示 | foreach で全件、Count で空判定 | 第 12 章 |
| 追加 | items.Add(name) | 第 12 章 |
| 削除 | items.Remove(name)(削除できれば true を返す) | 第 12 章 |
ポイント:
Listが「アプリの中身」を覚えているプログラムが動いている間、
itemsという 1 つのListが データを保持し続けます。 「追加」で増え、「削除」で減り、「一覧」で今の中身を見せる ── これは、第 23 章以降で作る 社員管理アプリ(一覧・追加・削除) とまったく同じ考え方です。 今は文字列のリストですが、List<Employee>に変えれば、そのまま「社員名簿アプリ」になります(演習の総合課題で挑戦します)。
〔前半のまとめ〕
Listだけでも、これだけできる
List<T>はforeachと組み合わせるだけで、一覧表示・検索・集計(合計 / 人数 / 平均)、さらに メニュー型のミニプログラム(一覧・追加・削除) まで作れます。 業務でも、まずはこのList+foreachが基本の道具です。ここまでを確実にできれば十分 です。
〔後半〕LINQ とラムダ式 ― もっと便利に(発展・少し難しい)
Section titled “〔後半〕LINQ とラムダ式 ― もっと便利に(発展・少し難しい)”ここから 後半 です。前半で foreach を使って書いた検索・並び替え・集計を、LINQ を使って短く書く方法を学びます。
ただし LINQ とラムダ式は少し難しい内容です。まずは「読めて、真似て書ける」ことを目標 にしてください。
難しいと感じたら、いったん前半の List + foreach に戻って大丈夫です。
【重要】LINQ は第 17 章までに「真似てでも使える」ように
ラムダ式の細かい仕組みまで完全に理解できていなくて構いません。しかし、第 17 章(データベース接続)以降では、LINQ を使ったコードが当たり前に登場します。 第 17 章に入るまでに、基本パターン(
Where/Select/Sum)を真似てでも書けるようになっておくことは必須 です。 今日この場で完璧でなくても大丈夫です(難しければ前半に戻ってOK)。ただしその場合も、自習日や復習で 必ずこのパターンを身につけてください。「foreach で十分だから LINQ は要らない」ではなく、「今は foreach で乗り切り、LINQ は後で必ず使えるようにする」と考えてください。
12-3 LINQ で絞り込む
Section titled “12-3 LINQ で絞り込む”LINQ とは
Section titled “LINQ とは”LINQ(リンク, Language INtegrated Query)は、コレクション(List<T> や配列など)に対して、検索・絞り込み・並び替え・集計を メソッド呼び出しで簡潔に書く ための仕組みです。
裏では特別なことは起きていません。次のようなイメージで動いています。
employees.Where(条件).ToList() ↓ 裏で行われていることforeach で 1 件ずつ取り出して、条件が true のものを集める ↓最後に .ToList() で List<T> として確定させるつまり LINQ は、前の節で書いた「foreach + if + 別の List に詰める」というパターンを、短く書くためのショートカット です。
書き方は変わりますが、行われていることは普段の繰り返し処理と同じです。
LINQ は System.Linq 名前空間にありますが、暗黙的 using で取り込まれているため using を書く必要はありません。
Where で絞り込む
Section titled “Where で絞り込む”Where は、条件に合う要素だけを取り出します。
namespace Ch12_ListLinq;
internal class Program{ static void Main(string[] args) { List<Employee> employees = EmployeeSampleData.Create();
List<Employee> salesEmployees = employees .Where(employee => employee.DepartmentName == "営業") .ToList();
foreach (Employee employee in salesEmployees) { Console.WriteLine($"{employee.EmployeeId}:{employee.EmployeeName}({employee.DepartmentName})"); } }}実行結果:
1002:佐藤昭夫(営業)1009:星野健一(営業)foreach で書いたコードと比べて、ぐっと短くなりました。
ラムダ式とは(まずはイメージから)
Section titled “ラムダ式とは(まずはイメージから)”Where の中に書いている employee => employee.DepartmentName == "営業" は ラムダ式 と呼ばれます。
ラムダ式は、ひとことで言うと 「名前を付けずに書いた、小さなメソッド」 です。
もし普通のメソッドとして書くなら、次のようになります。
static bool IsSales(Employee employee){ return employee.DepartmentName == "営業";}これと同じことを、Where の中だけで使い捨てる目的で 短く書いた のがラムダ式です。
employee => employee.DepartmentName == "営業"両者を並べて比べると、対応関係が見えます。
| メソッド式 | ラムダ式 |
|---|---|
メソッド名 IsSales | (省略・名前なし) |
戻り値の型 bool | (省略・自動で判定される) |
引数 Employee employee | employee(型も省略可) |
{ return ...; } | => ...(式だけ書く) |
このように、ラムダ式は メソッドを書く手間をぐっと省いた書き方 です。
わざわざ別の場所にメソッドを定義しなくても、Where の引数として その場で 1 行 で渡せます。
LINQ で出てくるラムダ式は 3 パターンだけ
Section titled “LINQ で出てくるラムダ式は 3 パターンだけ”最初は「LINQ メソッドの中で使うお決まりの書き方」と割り切るのが安全です。 本章で出てくるラムダ式は、次の 3 パターンに分類できます。
| 用途 | 返すもの | 例 | 使うメソッド |
|---|---|---|---|
| 条件判定 | bool(真偽値) | e => e.Salary >= 500000 | Where、FirstOrDefault、Count(条件) |
| 並び替えのキー | 比較に使う値 | e => e.Salary | OrderBy、OrderByDescending |
| 変換・取り出し | 取り出したい値 | e => e.EmployeeName | Select、Sum、Average |
ラムダ式そのものは、デリゲートや Func<T, bool> 型といった少し深い概念と結び付いていますが、本研修では深入りしません。
「LINQ メソッドの引数として、その場で使い捨てる小さな式」 くらいの理解で、後の DB 接続編・Web MVC 編でも十分に書けます。
ラムダ式の読み方
Section titled “ラムダ式の読み方”Where の中の式をもう一度見てみます。
employee => employee.DepartmentName == "営業"| 部分 | 意味 |
|---|---|
employee | リストから 1 件ずつ取り出した要素を入れる変数 |
=> | 「を使って」「を以下のように評価する」 |
employee.DepartmentName == "営業" | この式が true の要素だけ通す |
日本語にすると:
employee(1 件分の社員)を見て、DepartmentName が "営業" なら通すリストの各要素に対して、この式が true になるものだけ が Where の結果に含まれます。
ToList の役割
Section titled “ToList の役割”Where の戻り値は List<T> そのものではなく、「並び」を表す中間データ(IEnumerable<T>)です。
これを List<T> として受け取るには、最後に .ToList() を付けます。
List<Employee> salesEmployees = employees .Where(employee => employee.DepartmentName == "営業") .ToList(); // ← List<Employee> に変換ひとまずは「LINQ の結果は .ToList() で List<T> にする」と覚えればよいです。
FirstOrDefault で 1 件取得する
Section titled “FirstOrDefault で 1 件取得する”FirstOrDefault は、条件に合う 最初の 1 件 を取得します。見つからなかった場合は null を返します。
namespace Ch12_ListLinq;
internal class Program{ static void Main(string[] args) { List<Employee> employees = EmployeeSampleData.Create();
Employee employee = employees .FirstOrDefault(e => e.EmployeeId == 1002);
if (employee == null) { Console.WriteLine("社員が見つかりませんでした。"); } else { Console.WriteLine($"{employee.EmployeeId}:{employee.EmployeeName}"); } }}実行結果:
1002:佐藤昭夫FirstOrDefault の 「OrDefault」 が「見つからなければ既定値(参照型なら null)を返す」という意味です。
戻り値を使う前に、必ず null チェック を行いましょう。
補足:類似メソッド
First
Firstも最初の 1 件を取得しますが、見つからない場合は 例外 を投げます。 「見つからない可能性がある」場合はFirstOrDefaultの方が安全です。
Count(条件) で件数を数える
Section titled “Count(条件) で件数を数える”LINQ の Count(条件) で、条件に合う件数を数えられます。
List<Employee> employees = EmployeeSampleData.Create();
int salesCount = employees .Count(e => e.DepartmentName == "営業");
Console.WriteLine($"営業の人数:{salesCount}人");実行結果:
営業の人数:2人Count には 2 種類の使い方があるので注意してください。
| 書き方 | 種類 | 意味 |
|---|---|---|
employees.Count | プロパティ | リスト全体の件数 |
employees.Count(条件) | LINQ メソッド | 条件に合う件数 |
() の有無で別物だと意識しましょう。
12-4 LINQ で並び替え・集計を行う
Section titled “12-4 LINQ で並び替え・集計を行う”これ以降の例の書く場所について
ここから先のコードも、これまでと同じく
Mainの中 に書きます。最初にList<Employee> employees = EmployeeSampleData.Create();で社員リストを用意し、その下に LINQ のコードを書く前提です(Mainを含む完全な形は、12-3「Where で絞り込む」「FirstOrDefault で 1 件取得する」の例を参照)。 以降は、注目してほしい LINQ の部分だけ を抜き出して短く示します。
OrderBy で昇順に並び替える
Section titled “OrderBy で昇順に並び替える”OrderBy で、指定した項目で 昇順(小さい順) に並び替えます。
List<Employee> employees = EmployeeSampleData.Create();
List<Employee> sorted = employees .OrderBy(e => e.Salary) .ToList();
foreach (Employee employee in sorted){ Console.WriteLine($"{employee.EmployeeName}:{employee.Salary}円");}実行結果:
星野健一:400000円山田二郎:500000円佐藤昭夫:500000円山口洋子:500000円佐々木明子:800000円OrderByDescending で降順に並び替える
Section titled “OrderByDescending で降順に並び替える”降順(大きい順) にしたい場合は OrderByDescending を使います。
List<Employee> sorted = employees .OrderByDescending(e => e.Salary) .ToList();実行結果:
佐々木明子:800000円山田二郎:500000円佐藤昭夫:500000円山口洋子:500000円星野健一:400000円| メソッド | 並び順 |
|---|---|
OrderBy(項目) | 昇順(小さい順、古い順、あいうえお順) |
OrderByDescending(項目) | 降順(大きい順、新しい順、その逆) |
Sum で合計を求める
Section titled “Sum で合計を求める”Sum で、指定した数値項目の合計を求められます。
List<Employee> employees = EmployeeSampleData.Create();
int totalSalary = employees.Sum(e => e.Salary);
Console.WriteLine($"給与合計:{totalSalary}円");実行結果:
給与合計:2700000円Average で平均を求める
Section titled “Average で平均を求める”Average で平均を求められます。戻り値は double です。
double averageSalary = employees.Average(e => e.Salary);double rounded = Math.Round(averageSalary, 1);
Console.WriteLine($"平均給与:{rounded}円");実行結果:
平均給与:540000円桁数を抑えたい場合は、第 9 章で学んだ Math.Round と組み合わせます。
Select で必要な項目だけ取り出す
Section titled “Select で必要な項目だけ取り出す”Select で、各要素から 必要な項目だけ を取り出して新しいリストにできます。
List<Employee> employees = EmployeeSampleData.Create();
List<string> employeeNames = employees .Select(e => e.EmployeeName) .ToList();
foreach (string name in employeeNames){ Console.WriteLine(name);}実行結果:
山田二郎佐藤昭夫山口洋子佐々木明子星野健一Select は、画面表示用に必要な項目だけを取り出す場面や、形を変換する場面で使います。
複数条件で絞り込む
Section titled “複数条件で絞り込む”Where の条件は、&&(かつ)・||(または)で組み合わせられます。
次の例は、部署が “営業” かつ 給与が 500000 円以上 の社員を取り出します。
List<Employee> results = employees .Where(e => e.DepartmentName == "営業" && e.Salary >= 500000) .ToList();
foreach (Employee employee in results){ Console.WriteLine($"{employee.EmployeeName}:{employee.Salary}円");}実行結果:
佐藤昭夫:500000円該当データがない場合の対応
Section titled “該当データがない場合の対応”検索結果が 0 件の場合もあります。.Count で確認して、ユーザーに伝えます。
List<Employee> results = employees .Where(e => e.DepartmentName == "人事") .ToList();
if (results.Count == 0){ Console.WriteLine("該当する社員は見つかりませんでした。");}else{ foreach (Employee employee in results) { Console.WriteLine(employee.EmployeeName); }}実行結果:
該当する社員は見つかりませんでした。Web アプリ・デスクトップアプリでも、検索結果 0 件の表示は重要なポイントです。
Contains を使った部分一致検索
Section titled “Contains を使った部分一致検索”string の Contains を組み合わせると、部分一致検索 ができます。
Console.Write("検索する名前を入力してください:");string keyword = Console.ReadLine();
List<Employee> results = employees .Where(e => e.EmployeeName.Contains(keyword)) .ToList();
foreach (Employee employee in results){ Console.WriteLine($"{employee.EmployeeId}:{employee.EmployeeName}");}実行例:
検索する名前を入力してください:山1001:山田二郎1003:山口洋子12-5 LINQ をもっと使いこなすために
Section titled “12-5 LINQ をもっと使いこなすために”ここまで、LINQ の基本的なメソッドの使い方を 1 つずつ学んできました。 本節では、現場で書くときに役立つ 整理の視点 を 4 つ紹介します。細かい暗記は不要で、「こういうものがある」と知っておくだけで、後章のコード読みが楽になります。
メソッドチェーンで段階的に処理する
Section titled “メソッドチェーンで段階的に処理する”LINQ のメソッドは つなげて書く(メソッドチェーン)ことで威力を発揮します。 たとえば「営業部の社員を給与の高い順に並べて、氏名だけ取り出す」を書くと次のようになります。
List<string> result = employees .Where(e => e.DepartmentName == "営業") // ① 絞り込み .OrderByDescending(e => e.Salary) // ② 並び替え .Select(e => e.EmployeeName) // ③ 必要項目だけ取り出し .ToList(); // ④ List<string> に確定裏では次のような中間データが順に渡されていきます。
employees(5 件) ↓ Where で絞り込み[佐藤昭夫, 星野健一](2 件) ↓ OrderByDescending で並び替え[佐藤昭夫(500000), 星野健一(400000)] ↓ Select で氏名だけに変換["佐藤昭夫", "星野健一"] ↓ ToList で確定List<string>各メソッドが 前のメソッドの結果を受け取り、次に渡す 仕組みです。
順番が大事で、OrderBy を Where の前に置くと「全件並び替えてから絞り込む」になってしまい、無駄な処理が増えます。
慣れないうちは 絞り込み → 並び替え → 取り出し → ToList の順で覚えると安全です。
LINQ メソッドの一覧
Section titled “LINQ メソッドの一覧”本章で扱ったメソッドを中心に、現場でよく出るメソッドも含めて整理します。
| 分類 | メソッド | 役割 | 本章 |
|---|---|---|---|
| 絞り込み | Where(条件) | 条件に合う要素だけ通す | ◎ |
| 1 件取得 | FirstOrDefault(条件) | 最初の 1 件、なければ null | ◎ |
| 1 件取得 | First(条件) | 最初の 1 件、なければ例外 | ○ |
| 件数 | Count(条件) | 条件に合う件数 | ◎ |
| 存在チェック | Any(条件) | 1 件でも合えば true | △ |
| 全件チェック | All(条件) | すべてが合えば true | △ |
| 並び替え | OrderBy(キー) | 昇順 | ◎ |
| 並び替え | OrderByDescending(キー) | 降順 | ◎ |
| 第二キー | ThenBy(キー) | OrderBy の後の同点処理 | △ |
| 集計 | Sum(キー) | 合計 | ◎ |
| 集計 | Average(キー) | 平均 | ◎ |
| 集計 | Min(キー) / Max(キー) | 最小 / 最大 | △ |
| 取り出し | Select(変換) | 各要素を変換 | ◎ |
| 確定 | ToList() | List<T> に変換 | ◎ |
| 確定 | ToArray() | 配列に変換 | △ |
◎ は本章で扱い、○ は補足程度に触れ、△ は本章では扱いません。
△ のメソッドは、必要になったときに名前で検索すれば使い方がすぐに見つかります。全部を今覚える必要はありません。
LINQ と foreach の使い分け
Section titled “LINQ と foreach の使い分け”LINQ は便利ですが、何でも LINQ で書けばよい わけではありません。
場面によっては foreach のほうが読みやすいことがあります。
| 場面 | おすすめ | 理由 |
|---|---|---|
| 単純な絞り込み・並び替え・集計 | LINQ | 短く読みやすい |
| 各要素ごとに副作用がある処理(画面表示、ログ出力、DB 書き込み) | foreach | リストを作るのが目的ではないため |
| 集計と表示を同時に行うなど、複数の処理を 1 周で済ませたい | foreach | LINQ に詰め込むと読みにくくなる |
中断が必要(break、途中で return) | foreach | LINQ では基本的に中断できない |
| 例外処理を細かく挟みたい | foreach | LINQ の中に try-catch は書きにくい |
ひとつの目安:「リスト → リスト」「リスト → 1 件 / 数値」のような データの変換・集計 は LINQ、「リストの各要素に対して何かを実行する」は foreach。
もう一つの書き方:クエリ構文
Section titled “もう一つの書き方:クエリ構文”LINQ には、本章で扱った メソッド構文 の他に、SQL に似た クエリ構文 という書き方もあります。
// メソッド構文(本章で学んだ書き方)List<Employee> sales = employees .Where(e => e.DepartmentName == "営業") .OrderBy(e => e.Salary) .ToList();
// クエリ構文List<Employee> sales = ( from e in employees where e.DepartmentName == "営業" orderby e.Salary select e).ToList();from ... in ... where ... orderby ... select ... の構文は SQL の SELECT 文と並びが似ている ため、SQL 研修受講者には読みやすく感じるかもしれません。
ただし、現場では圧倒的にメソッド構文のほうが多く使われます。本研修でもメソッド構文を基本としますが、現場のコードでクエリ構文を見たときに驚かないよう、ここで紹介しておきます。
両者は最終的に同じ結果になり、コンパイラがクエリ構文をメソッド構文に変換しています。
12-6 DB 連携に向けた List<T> の考え方
Section titled “12-6 DB 連携に向けた List<T> の考え方”DB の 1 行 = 1 つのオブジェクト
Section titled “DB の 1 行 = 1 つのオブジェクト”後の DB 接続編では、データベースの employees テーブルから取得したデータを、次のように List<Employee> で扱います。
employees テーブル(DB)
employee_id | employee_name | department_id | salary------------|---------------|---------------|-------1001 | 山田二郎 | 1 | 5000001002 | 佐藤昭夫 | 2 | 5000001003 | 山口洋子 | 3 | 500000上記のイメージは「列名と値の組み合わせ」を分かりやすくするための簡略表記です。第 17 章で扱う SQLServer のテーブルでは
last_name(姓)とfirst_name(名)を分けて持ちますが、ここでは LINQ の流れに集中するため 1 列(employee_name)にまとめてあります。
SELECT 結果 ↓ 1 行ずつ読み取るnew Employee { EmployeeId = ..., EmployeeName = ..., ... } ↓ AddList<Employee> employees ↓ LINQ検索・絞り込み・並び替え・集計つまり、DB 接続編に進む前に List<Employee> と LINQ に慣れておく ことが重要です。
この章で学んだ書き方は、DB 連携でもそのまま使えます。
Web・デスクトップアプリでの利用イメージ
Section titled “Web・デスクトップアプリでの利用イメージ”List<Employee> は、画面表示でも頻繁に使います。
- Web アプリ:Controller から View へ
List<Employee>を渡し、一覧表示する - デスクトップアプリ:
List<Employee>をDataGridViewのDataSourceに設定して表形式で表示する
データベース(SQLServer / Oracle) ↓List<Employee> ↓画面の一覧(Web の表、Forms の DataGridView)この章の内容は、研修後半のアプリ作成にそのままつながります。
よくあるつまずき
Section titled “よくあるつまずき”| つまずき | 原因 | 対応 |
|---|---|---|
List<T> の T が分からない | ジェネリック型に慣れていない | T には扱う型を書く(List<int>、List<Employee>) |
Add を忘れて空のリストになる | リストに要素を追加していない | list.Add(...) を確認 |
Count と Count() を混同 | プロパティと LINQ メソッドが混ざる | 全件は Count、条件付きは Count(条件) |
Where の結果を List<T> に代入できない | ToList() を忘れている | .Where(...).ToList() と書く |
| ラムダ式が読めない | => の意味に慣れていない | 「1 件取り出して条件を見る」と読む |
FirstOrDefault の結果でエラー | 見つからず null なのにプロパティを使っている | 必ず null チェック |
| 部分一致検索で大文字小文字が違う | Contains は大小文字を区別する | 必要なら ToLower() で揃える |
OrderBy の結果が並び替わらない | ToList() で受けていない | 必ず .ToList() を付ける |
| LINQ を 1 行に詰め込みすぎる | 読みにくい | .Where(...).OrderBy(...).ToList() のように改行する |
学んだことチェック
Section titled “学んだことチェック”前半:List<T>(まずはここを確実に)
-
List<T>とは何かを説明できる - 配列と
List<T>の違いを説明できる -
List<int>・List<string>・List<Employee>を作成できる -
Add・Count・Removeを使える - サンプルデータ用の静的クラス・メソッドを書ける
-
foreachで検索・集計(合計・人数・平均)ができる
後半:LINQ とラムダ式(できれば)
- LINQ とは何かを説明できる
-
Whereで絞り込みができる - ラムダ式の基本的な読み方が分かる
-
ToList()の役割を説明できる -
FirstOrDefaultで 1 件取得し、nullチェックできる -
OrderBy・OrderByDescendingで並び替えできる -
Sum・Average・Count(条件)で集計できる -
Selectで必要な項目だけを取り出せる - DB の検索結果を
List<Employee>で扱うイメージを説明できる
研修の進め方によっては、隣の人またはチーム内で説明確認を行います。
次の内容を、自分の言葉で説明してください。
- 配列と
List<T>の違いは何ですか。 List<Employee>は何を表していますか。employees.Countとemployees.Count(条件)の違いは何ですか。Whereはどんなときに使いますか。- ラムダ式
employee => employee.Salary >= 500000はどう読みますか。 FirstOrDefaultの結果を使う前に注意することは何ですか。Selectはどんなときに使いますか。- DB から取得した複数件のデータを、C# では何で受けますか。
説明するときは、完全な答えでなくても構いません。 自分の言葉で説明しようとすることが大切です。
この章の演習課題に取り組みます。
本章では タイマー方式 で進めます。さらに本章から、ソロ作業の前に 設計タイム(コメント・枠づくり) を試験導入します。次の 3 段階で進めてください。
| 段階 | 時間 | 内容 |
|---|---|---|
| ① 設計タイム | 15 分(評価対象外) | ペア確認・課題(仕様)の読み込みに加え、コメントとクラス/メソッドの枠づくり を行う。ペアや講師に相談してよい(詳細は下記) |
| ② ソロ作業 | 30 分(タイマーで計測) | 一人で課題に取り組む。枠の下に実装ロジックを書く。タイマーが鳴ったら、完成・未完成にかかわらず作業を止めて提出する |
| ③ チーム時間 | 講師が指定する発表開始時刻まで | チーム内でコードレビューを行い、発表者を決める。実装の続行も可。時間配分はチームで管理する |
① 設計タイムで作ってよいもの(評価対象外・ペアで相談OK)
ソロのタイマーに入る前に、ペアやグループで相談しながら 次まで作ってよい です(作ったコメント・枠は ソロのコミットにそのまま残してOK)。
- ヘッダーコメント(課題番号・目的・氏名。第 7 章コラム参照)
- 処理の流れを示す 意図コメント(例:
// products を Where で文房具だけに絞る)- クラス・メソッドの 枠(
Productクラスのプロパティ宣言、メソッドのシグネチャと空の{ })- プロジェクト・クラスファイル(
Product.cs・ProductSampleData.csなど)の用意ただし 枠を作ってよいのは「問題文が指定している範囲」まで です。必須課題(12-2・12-3)は
Productのプロパティや扱うデータが問題文の表で示されているので、その枠は写して作って構いません。発展課題(12-4〜12-7)で どんなクラス・処理に分けるかを自分で考える部分は、設計タイムでは意図コメント(処理の流れのメモ)までにとどめ、枠はソロで 作ってください。迷ったら「その枠は問題文に書いてあるか?」で判断します。タイマーで測るのは、枠の下に各自が書く「実装ロジック(LINQ や foreach の中身)」 です。設計はみんなで考え、実装はひとりで書きます。詳しくは 付録 A「演習サイクルとチームレビューの進め方」A-2 を参照してください。
第 12 章は LINQ メソッドを多用し、課題ごとにクラス(Product.cs など)とサンプルデータ用クラス(ProductSampleData.cs など)を別ファイルで作る作業が発生します。設計タイムでこれらの枠とサンプルデータの用意まで済ませておくと、ソロでは LINQ・foreach の中身に集中できます。設計タイムの終わりに講師が号令をかけ、そこからソロのタイマー(30 分)を開始します。
評価対象はタイマー時点で提出されたコードです(タイマー後に書き足した分は評価には含まれません)。設計タイムで作ったコメント・枠は、ソロのコミットに含めて構いません。
発表開始時刻は厳守 です。チーム時間中も、その時刻が来たら全員手を止めて発表に移ります。
演習の進め方の詳細は、付録A「演習の進め方」 を参照してください。
この章の演習の進め方
Section titled “この章の演習の進め方”課題はソリューション Kadai12 の中に作成してください。
課題ごとに別のプロジェクトを作成 し、指定されたプロジェクト名を使います。
| 課題 | 必須/発展 | プロジェクト名 | 作成する主なファイル |
|---|---|---|---|
| 課題 12-1 | 必須 | Kd12_01_ListBasic | Program.cs |
| 課題 12-2 | 必須 | Kd12_02_ProductListSearch | Program.cs、Product.cs、ProductSampleData.cs |
| 課題 12-3 | 必須 | Kd12_03_SortAndAggregate | Program.cs、Product.cs、ProductSampleData.cs |
| 課題 12-4 | 発展 | Kd12_04_PartialMatch | Program.cs、Product.cs、ProductSampleData.cs |
| 課題 12-5 | 発展 | Kd12_05_SelectAndFilter | Program.cs、Product.cs、ProductSampleData.cs |
| 課題 12-6 | 発展 | Kd12_06_DbStyleEmployee | Program.cs、Employee.cs |
| 課題 12-7 | 総合・挑戦 | Kd12_07_EmployeeRoster | Program.cs、Employee.cs、EmployeeSampleData.cs |
フォルダ構成は次のようになります。
Kadai12/ ← 課題用ソリューションフォルダ Kadai12.sln Kd12_01_ListBasic/ Kd12_02_ProductListSearch/ Kd12_03_SortAndAggregate/ Kd12_04_PartialMatch/ Kd12_05_SelectAndFilter/ Kd12_06_DbStyleEmployee/ Kd12_07_EmployeeRoster/補足:ソリューションに複数のプロジェクトを追加する方法
最初の課題で
Kadai12ソリューションとKd12_01_ListBasicプロジェクトを同時に作成します。2 つ目以降の課題は、ソリューションエクスプローラーで
Kadai12を右クリックし、追加→新しいプロジェクトから追加します。
演習の共通ルール
Section titled “演習の共通ルール”以下は、本章のすべての課題に共通する作業です。各課題の本文には繰り返し書きません。
| 作業 | 内容 | 参照 |
|---|---|---|
| プロジェクト作成時の設定 | 「最上位レベルのステートメントを使用しない」にチェック | 第 1 章 1-1 |
| csproj の編集 | <Nullable>disable</Nullable> に変更 | 第 1 章 1-1 |
namespace の書き方 | ファイルスコープ形式(namespace XXX;)で書く | 第 7 章 冒頭 |
| クラスファイルの追加 | ソリューションエクスプローラーでプロジェクトを右クリック → 追加 → クラス | 第 7 章 7-7 |
| ファイルを保存して実行 | Ctrl + S で保存 → F5 で実行 | 第 1 章 1-2 |
特に 2 つ目以降のプロジェクト(Kd12_02_ProductListSearch など)も、新規追加するたびに csproj の Nullable を disable に変更する 必要があります。
クラスファイルを自動生成すると、ブロック形式の namespace プロジェクト名 { ... } で作られます(例:課題 12-2 なら namespace Kd12_02_ProductListSearch { ... })。本章でもファイルスコープ形式に統一するため、生成後に namespace Kd12_02_ProductListSearch; のように ; 形式に書き換えてください。同じプロジェクト内の Program.cs とクラスファイル(Product.cs・ProductSampleData.cs など)の namespace は揃えます。
提出ルール(タイマー方式)
30 分のタイマーが鳴ったら手を止め、
git add→commit→pushを行います。評価対象はタイマー時点のコミットのみです(タイマー後の追加は評価外)。Chapter12 タイマー提出: <どこまで完成> / <詰まったポイント>例:
Chapter12 タイマー提出: 12-1〜12-2完成、12-3は途中 / OrderByDescending と ToList の書き方で詰まった
サーバへコピー提出の場合
Git 不調で講師指示があれば、
pushの代わりにKadai12フォルダをサーバへコピーし、提出先に提出メモ.txtを作成します(内容:どこまで完成 / 詰まったポイント)。
タイマー後のチーム時間
発表開始時刻まで、コードレビュー / 発表者選出 / 実装続行(任意・評価外)。時間配分はチームの判断、発表開始時刻は厳守。
まずは、全員が必須課題に取り組んでください。
必須課題の進め方
必須課題は、並び替えを除いて
foreachだけで一通り実装できます。LINQ がまだ不安な人は、まずforeachで完成させてください。 LINQ(Where/FirstOrDefault/Sumなど)で書ければ、なお良し です。両方書いて見比べると、LINQ の短さが実感できます。 (並び替えだけは、foreachで自前実装すると大変なのでOrderByDescendingを使います。LINQ が一番活きる処理です。)
課題 12-1 List で点数と部署を管理する
Section titled “課題 12-1 List で点数と部署を管理する”List<int> で点数、List<string> で部署名を管理するプログラムを作成してください。
処理内容
List<int>に80, 75, 90, 68, 85を追加し、Countで件数を表示してから全件を表示するList<string>を初期値あり(総務, 営業, 開発, マーケティング)で作成し、Countで件数を表示してから全件を表示する
実行結果例:
[点数]件数:58075906885
[部署]件数:4総務営業開発マーケティング条件:
List<int>にはAddで追加するList<string>は初期値ありで作成する- どちらも
Countプロパティで件数を表示する foreachで全件表示する
課題 12-2 商品リストの検索
Section titled “課題 12-2 商品リストの検索”Product(商品)クラスと、サンプルデータ作成用の ProductSampleData 静的クラスを作成し、商品を検索してください。
foreach + if で実装してOK。余裕があれば Where / FirstOrDefault でも書いてみましょう。
Product クラスのプロパティ
| プロパティ名 | 型 | 意味 |
|---|---|---|
ProductId | int | 商品番号 |
ProductName | string | 商品名 |
Category | string | カテゴリ |
Price | int | 価格(円) |
Stock | int | 在庫数 |
ProductSampleData.Create() で返す 6 件分のデータ
| ID | 商品名 | カテゴリ | 価格 | 在庫 |
|---|---|---|---|---|
| 1 | ノート | 文房具 | 180 | 50 |
| 2 | ボールペン | 文房具 | 120 | 100 |
| 3 | サインペン | 文房具 | 200 | 40 |
| 4 | マグカップ | 食器 | 800 | 20 |
| 5 | タンブラー | 食器 | 1700 | 8 |
| 6 | Tシャツ | 衣類 | 2400 | 15 |
Main メソッドの処理
ProductSampleData.Create()でList<Product>を取得- カテゴリが「食器」の商品を抽出して全件表示(
foreach+ifでよい /Whereでも可) ProductId == 4の商品を 1 件取得し、見つかれば商品名を表示、見つからなければ「商品が見つかりませんでした。」と表示(foreach+ifでよい /FirstOrDefaultでも可)
実行結果例:
[食器の商品]4:マグカップ5:タンブラー
[ID 4 検索]マグカップ条件:
Product.csとProductSampleData.csを別ファイルに作成- まずは
foreach+ifで実装してよい(食器の抽出・ID 検索) - 余裕があれば
Where/FirstOrDefaultでも書いてみる(同じ結果を短く書けることを確認) - 見つからない場合(該当なし /
null)の処理を必ず入れる
課題 12-3 並び替えと集計
Section titled “課題 12-3 並び替えと集計”課題 12-2 の ProductSampleData を使い、価格で並び替えと集計を行ってください。
処理内容
- 価格の降順 で全商品を表示 ← 並び替えは
OrderByDescendingを使う(foreachで自前ソートは大変。ここは LINQ が圧倒的に簡単) - 在庫の合計 を表示 ←
foreachで足し込んでよい(Sumならなお良し) - 平均価格 を表示 ←
合計 ÷ 件数で求める(Averageならなお良し)
実行結果例:
[価格の高い順]Tシャツ:2400円タンブラー:1700円マグカップ:800円サインペン:200円ノート:180円ボールペン:120円
在庫合計:233個平均価格:900円条件:
- 並び替えは
OrderByDescending+ToList()を使う(LINQ が圧倒的に簡単な処理。ここだけは LINQ) - 集計(在庫合計・平均価格)は
foreachでもSum/Averageでもよい - 平均に小数が出る場合は
Math.Roundで調整してよい(今回のデータは割り切れる)
必須課題が終わった人は、発展課題に取り組んでください。 発展課題からは、仕様だけが提示されます。実装方法は自分で考えてください。
発展課題は LINQ の練習場です
発展課題は、後半で学んだ LINQ(
Where/Select/Sumなど)を使って書く 練習として取り組んでください。foreachでも書けますが、ここでは「LINQ で短く書く」ことに挑戦するのが目的です。
課題 12-4 商品名の部分一致検索
Section titled “課題 12-4 商品名の部分一致検索”以下の仕様で、商品名にキーワードを含む商品を検索するプログラムを作成してください。
仕様
Console.ReadLineで検索キーワードを 1 行受け取る- 前後の空白を
Trimで取り除く Where+Containsで部分一致検索を行う- 検索結果が 0 件なら「該当する商品は見つかりませんでした。」と表示
- 1 件以上なら、
ProductIdの昇順で並び替えてから一覧表示
サンプルデータは課題 12-2 と同じ(ProductSampleData.Create() を再利用)。
実行例:
検索キーワードを入力してください:ペン2:ボールペン(文房具)3:サインペン(文房具)検索キーワードを入力してください:時計該当する商品は見つかりませんでした。課題 12-5 Select で必要な項目だけ取り出す + 複数条件
Section titled “課題 12-5 Select で必要な項目だけ取り出す + 複数条件”以下の仕様でプログラムを作成してください。
仕様
サンプルデータは課題 12-2 と同じ。 次の 2 つを順番に行う。
- 複数条件絞り込み:カテゴリが「文房具」かつ 価格が 150 円以上の商品を、
Whereで絞り込み、ProductNameとPriceを表示する - Select で商品名のみ取り出し:全商品から
Selectを使ってProductNameだけのList<string>を作り、foreachで全件表示する
実行結果例:
[文房具 かつ 価格150以上]ノート:180円サインペン:200円
[全商品の商品名]ノートボールペンサインペンマグカップタンブラーTシャツ条件:
Whereの条件で&&を使うSelectでList<string>を作る
課題 12-6 DB 接続を想定した Employee リスト
Section titled “課題 12-6 DB 接続を想定した Employee リスト”後の DB 接続編を想定し、null 許容値型を含む Employee クラスとそのリスト処理を作成してください。
Employee クラス仕様
| プロパティ名 | 型 | 意味 |
|---|---|---|
EmployeeId | int | 社員番号 |
EmployeeName | string | 氏名 |
HireDate | DateTime | 入社日 |
Salary | decimal | 給与 |
Commission | decimal? | 歩合(null あり) |
ManagerId | int? | 上司の社員番号(null あり) |
DepartmentId | int | 部署番号 |
引数なしの自動プロパティでよい(オブジェクト初期化子で作成する想定)。
サンプルデータ(List<Employee>)
| ID | 氏名 | 入社日 | 給与 | 歩合 | 上司ID | 部署ID |
|---|---|---|---|---|---|---|
| 1001 | 山田二郎 | 2001-04-01 | 500000 | null | null | 1 |
| 1002 | 佐藤昭夫 | 2003-04-01 | 500000 | 1500000 | 1001 | 2 |
| 1006 | 佐々木明子 | 1995-04-01 | 800000 | 2000000 | null | 1 |
Main メソッドの処理
- 全社員を
EmployeeId:EmployeeName:給与{Salary}円:歩合{歩合 or 未設定}の形で一覧表示 - 歩合がある社員だけ(
Commission != null)をWhereで抽出して表示 - 全社員の歩合の合計 を求めて表示(
Sum、null の場合は 0 として合算する)
実行結果例:
[全社員]1001:山田二郎:給与500000円:歩合未設定1002:佐藤昭夫:給与500000円:歩合1500000円1006:佐々木明子:給与800000円:歩合2000000円
[歩合あり社員]1002:佐藤昭夫:1500000円1006:佐々木明子:2000000円
歩合合計:3500000円条件:
decimal?とint?を使うnullの表示には??またはif文を使うSumの中で null を 0 として扱う(Sum(e => e.Commission ?? 0))
課題 12-7 【総合・挑戦】社員名簿ミニプログラム(一覧・追加・削除 + LINQ で機能追加)
Section titled “課題 12-7 【総合・挑戦】社員名簿ミニプログラム(一覧・追加・削除 + LINQ で機能追加)”本文「List で動くミニプログラム(買い物メモ)」の 社員版 を作ります。
List<Employee> を使い、社員の 一覧表示・追加・削除 ができるメニュー型プログラムを作成してください。
さらに、LINQ を使うと「検索・並び替え・集計」メニューを増やせます。
これは総合課題(挑戦)です。本章の前半と後半を 1 つに束ねます
基本の 一覧・追加・削除は
List+foreachだけ で作れます(前半の力)。 そこに LINQ を足すと、検索・並び替え・平均などの便利メニューを増やせます(後半の力)。LINQ を真似て使う、絶好の練習です。 規模が大きいので、時間内に完成しなくても OK。まず基本(1〜3)を動かし、余裕があれば LINQ メニュー(4〜6)に挑戦してください。完成すれば、第 23 章以降で作る社員管理アプリの コンソール版 を自力で書いたことになります。
Employee クラス(課題 12-2 と同じ)
| プロパティ名 | 型 |
|---|---|
EmployeeId | int |
EmployeeName | string |
DepartmentName | string |
Salary | int |
メニューと動作
=== 社員名簿 ===[基本] 1: 一覧表示 2: 追加 3: 削除[LINQ] 4: 部署で検索 5: 給与の高い順 6: 平均給与 0: 終了> _基本(必達・List + foreach で作れる)
- 起動時に、初期データとして
EmployeeSampleData.Create()の社員を入れておいてよい - 1 一覧表示:全社員を
1001:山田二郎(総務)500000円の形で 1 行ずつ表示(foreach)。0 件なら「(名簿は空です)」 - 2 追加:社員番号・氏名・部署・給与を順に入力し、
new Employee { ... }を作ってAdd - 3 削除:社員番号を入力し、その番号の社員を
foreachで探してRemove(見つからなければ「見つかりませんでした」) - 0 終了:ループを抜けて終了
LINQ で機能を増やす(挑戦・各メニューは LINQ 1 行が中心)
- 4 部署で検索:部署名を入力 →
Where(e => e.DepartmentName == 入力)で絞り込んで一覧表示 - 5 給与の高い順:
OrderByDescending(e => e.Salary)で並び替えて一覧表示 - 6 平均給与:
Average(e => e.Salary)で平均を表示 - (さらに余裕があれば)名前で部分一致検索:
Where(e => e.EmployeeName.Contains(キーワード))
各 LINQ メニューは、基本のメニューループに else if を 1 つ足し、その中に 12-3・12-4 で学んだ LINQ を 1 行 書くだけです。
実行例:
=== 社員名簿 ===[基本] 1: 一覧表示 2: 追加 3: 削除[LINQ] 4: 部署で検索 5: 給与の高い順 6: 平均給与 0: 終了> 4部署名:営業1002:佐藤昭夫(営業)500000円1009:星野健一(営業)400000円
> 51006:佐々木明子(総務)800000円1001:山田二郎(総務)500000円...
> 6平均給与:540000円
> 0終了します。条件・ヒント:
- メニューの繰り返しは
while (true)、0でbreak(本文の買い物メモと同じ形) - 数値の入力(社員番号・給与)は
int.Parseを使う(正しい値が入力される前提でよい。int.TryParseで不正入力に備えるのは第 15 章で学びます) - 削除は「社員番号で該当する社員を
foreachで探してRemoveする」方法でよい(List+foreachだけで実装可能) - 検索・並び替え・平均(4〜6)は、
Where/OrderByDescending/Averageを 1 行書いて、結果をforeachで表示するだけ。12-3・12-4 のコードをそのまま真似て構いません(LINQ を実際に使ってみる絶好の機会です) - さらに余裕があれば、メニュー判定を
if/else ifからswitchに書き換えてみましょう。switchのbreakではループを抜けられないので、終了の仕方を工夫する必要があります(boolのフラグやreturnを使う)。制御フローの良い練習になります(任意)
提出前チェックリスト
Section titled “提出前チェックリスト”- プログラムを Visual Studio から実行できる
-
List<int>・List<string>・List<Employee>を使えている -
Add・Countを使えている -
Whereで絞り込みできている -
FirstOrDefaultで 1 件取得し、nullチェックできている -
OrderByまたはOrderByDescendingを使えている -
SumやAverageを使えている -
Selectを使えている(発展) - ラムダ式の意味を理解している
-
.ToList()で結果をList<T>にできている - 課題で使うクラス(
Product/Employeeなど)とサンプルデータ用クラスを別ファイルに書けている - インデントが整っている
- 課題を提出した(Git の
commit→push、または講師指示があればKadai12フォルダをサーバへコピーし、提出先フォルダに提出メモ.txtを作成)
Git への提出
Section titled “Git への提出”タイマーが鳴ったら、第 12 章の課題プロジェクト群(Kadai12 ソリューション)をその場で Git に提出します。
git statusgit add .git commit -m "Chapter12 タイマー提出: <どこまで完成> / <詰まったポイント>"git push実行例:
git commit -m "Chapter12 タイマー提出: 12-1〜12-2完成、12-3は途中 / OrderByDescending と ToList の書き方で詰まった"Git の詳しい操作は 付録 C、コミットメッセージ形式とサーバコピー提出は本章冒頭「演習課題 > 提出ルール」を参照してください。
この章のまとめ
Section titled “この章のまとめ”この章では、List<T> と LINQ を学習しました。
前半:List<T> だけでできること(まずはここを確実に)
List<T>は同じ型のデータを複数管理するコレクションTにはint・stringだけでなく自作クラスも指定できるAdd・Count・Remove・インデックスアクセスで要素を扱える- 配列より追加・削除がしやすい
List+foreachだけで、一覧表示・検索・集計(合計 / 人数 / 平均)まで実用的にこなせる(LINQ がなくても十分)
後半:LINQ とラムダ式(基本パターンは第 17 章までに必須・読めて真似て書ければOK)
- LINQ は、前半の
foreachの検索・絞り込み・並び替え・集計を簡潔に書く仕組み - LINQ は第 17 章以降の DB・Web 編で当たり前に登場する。第 17 章までに基本パターン(
Where/Select/Sum)を真似てでも書けるようにしておくことは必須 Whereで絞り込み、.ToList()でList<T>に変換- ラムダ式
e => 条件は「1 件取り出して条件を判定」と読む FirstOrDefaultは最初の 1 件を取得、見つからなければnullOrderBy/OrderByDescendingで並び替えSum/Averageで集計Countはプロパティ(全件)とメソッド(条件付き件数)の 2 種類Selectで必要な項目だけ取り出せる- DB 接続編では、SELECT 結果を
List<Employee>で受け、LINQ で扱う
次章では、継承とポリモーフィズム を学習します。
クラス同士の共通点をまとめる 継承、同じ呼び出し方で型に応じた動きをさせる ポリモーフィズム を扱います。
現場の既存コードを読むうえでも重要な考え方で、Employee をベースに Manager(管理職)や PartTimeEmployee(パート社員)などの派生クラスを作る場面で活躍します。