第12章 List<T> と LINQ
この章の目的
Section titled “この章の目的”この章では、複数のデータを柔軟に扱うための List
第6章では、配列を使って複数の値を管理しました。
int[] scores = { 80, 75, 90, 68, 85 };配列は、複数の値をまとめて扱うために便利です。
しかし、配列には次のような特徴があります。
作成時に要素数が決まる後から要素を追加しにくい削除や検索を自分で書く必要がある一方、List<T> を使うと、後からデータを追加したり、削除したりしやすくなります。
List<int> scores = new List<int>();
scores.Add(80);scores.Add(75);scores.Add(90);さらに、LINQを使うと、複数のデータから条件に合うものを取り出したり、並び替えたりできます。
List<Employee> salesEmployees = employees .Where(employee => employee.Department == "営業部") .ToList();後の章では、Oracle Databaseの employees 表から取得した複数件の社員情報を、List<Employee> として扱います。
この章は、DB連携、Webアプリ、デスクトップアプリにつながる重要な準備です。
この章でできるようになること
Section titled “この章でできるようになること”この章を終えると、次のことができるようになります。
List<T>とは何かを説明できる- 配列と
List<T>の違いを説明できる List<int>やList<string>を作成できるAddを使って要素を追加できるCountを使って要素数を取得できるforeach文でList<T>の要素を処理できるList<Employee>のように、自作クラスのリストを扱える- LINQとは何かをおおまかに説明できる
Whereを使って条件に合うデータを絞り込めるFirstOrDefaultを使って条件に合う最初のデータを取得できるOrderBy、OrderByDescendingで並び替えできるSelectを使って必要な項目だけを取り出せるSum、Average、Countを使って集計できるnullの可能性を意識して検索結果を扱える
本章で使用する環境
Section titled “本章で使用する環境”| 項目 | 内容 |
|---|---|
| 開発環境 | Visual Studio 2022 |
| プロジェクト種類 | コンソール アプリ |
| 対象フレームワーク | .NET 8 |
| プロジェクト名 | Chapter12_ListLinq |
作業前チェック
Section titled “作業前チェック”作業を始める前に、次の内容を確認してください。
- 配列を使って複数の値を扱える
-
foreach文を使える - クラスを作成できる
- プロパティを定義できる
- オブジェクト初期化子を使える
-
nullの意味を説明できる -
Employee?のようなnull許容参照型をおおまかに理解している - 第11章の内容をGitに提出済みである
12-1 Listとは?
Section titled “12-1 Listとは?”配列は、同じ型の複数の値をまとめて扱う仕組みです。
using System;
class Program{ static void Main() { int[] scores = { 80, 75, 90 };
foreach (int score in scores) { Console.WriteLine(score); } }}実行結果:
807590配列は便利ですが、作成時に要素数が決まります。
int[] scores = new int[3];この場合、基本的には3件分の領域として考えます。
後から自由に件数を増やしたい場合は、List<T> の方が扱いやすくなります。
Listを使う
Section titled “Listを使う”List<T> は、複数のデータを管理するためのコレクションです。
T の部分には、扱いたい型を指定します。
| 書き方 | 意味 |
|---|---|
List<int> | int 型の値を複数管理する |
List<string> | string 型の値を複数管理する |
List<Employee> | Employee 型のオブジェクトを複数管理する |
List<T> を使うには、通常、次の using を書きます。
using System.Collections.Generic;Listを作成する
Section titled “Listを作成する”次のコードを入力して実行してください。
using System;using System.Collections.Generic;
class Program{ static void Main() { List<int> scores = new List<int>();
scores.Add(80); scores.Add(75); scores.Add(90);
foreach (int score in scores) { Console.WriteLine(score); } }}実行結果:
807590Add メソッドを使うことで、リストに値を追加できます。
scores.Add(80);Listの要素数を調べる
Section titled “Listの要素数を調べる”List<T> の要素数は、Count プロパティで取得できます。
using System;using System.Collections.Generic;
class Program{ static void Main() { List<int> scores = new List<int>();
scores.Add(80); scores.Add(75); scores.Add(90);
Console.WriteLine($"件数:{scores.Count}"); }}実行結果:
件数:3配列では要素数を Length で取得しました。
scoresArray.LengthList<T> では Count を使います。
scores.Countどちらもプロパティなので、通常 () は付けません。
初期値を入れてListを作る
Section titled “初期値を入れてListを作る”List<T> は、最初から値を入れて作ることもできます。
using System;using System.Collections.Generic;
class Program{ static void Main() { List<string> departments = new List<string> { "総務", "営業", "開発", "マーケティング" };
foreach (string department in departments) { Console.WriteLine(department); } }}実行結果:
総務営業開発マーケティングインデックスでアクセスする
Section titled “インデックスでアクセスする”List<T> も配列と同じように、インデックスで要素にアクセスできます。
using System;using System.Collections.Generic;
class Program{ static void Main() { List<string> departments = new List<string> { "総務", "営業", "開発" };
Console.WriteLine(departments[0]); Console.WriteLine(departments[1]); Console.WriteLine(departments[2]); }}実行結果:
総務営業開発配列と同じように、インデックスは0から始まります。
Removeで削除する
Section titled “Removeで削除する”Remove メソッドを使うと、指定した値を削除できます。
using System;using System.Collections.Generic;
class Program{ static void Main() { List<string> departments = new List<string> { "総務", "営業", "開発" };
departments.Remove("営業");
foreach (string department in departments) { Console.WriteLine(department); } }}実行結果:
総務開発Remove は、指定した値が見つかった場合に削除します。
配列とListの違い
Section titled “配列とListの違い”| 項目 | 配列 | List |
|---|---|---|
| 要素数 | 基本的に固定 | 追加・削除しやすい |
| 要素数の取得 | Length | Count |
| 要素の追加 | やや面倒 | Add |
| 要素の削除 | やや面倒 | Remove、RemoveAt |
| 向いている場面 | 件数が決まっているデータ | 件数が変わるデータ |
後のDB接続では、SELECT文の結果が何件になるか実行してみないと分からないことがあります。
そのため、DBから取得した複数件のデータは、配列よりも List<T> で扱う方が自然です。
12-2 Listを使う
Section titled “12-2 Listを使う”自作クラスのリスト
Section titled “自作クラスのリスト”List<T> の T には、自分で作ったクラスも指定できます。
ここでは、Employee クラスのリストを作成します。
using System;using System.Collections.Generic;
class Program{ static void Main() { List<Employee> employees = new List<Employee>();
employees.Add(new Employee { EmployeeId = 1001, EmployeeName = "山田太郎", DepartmentName = "総務" });
employees.Add(new Employee { EmployeeId = 1002, EmployeeName = "佐藤花子", DepartmentName = "営業" });
employees.Add(new Employee { EmployeeId = 1003, EmployeeName = "鈴木一郎", DepartmentName = "開発" });
foreach (Employee employee in employees) { Console.WriteLine($"{employee.EmployeeId}:{employee.EmployeeName}({employee.DepartmentName})"); } }}
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } = ""; public string DepartmentName { get; set; } = "";}実行結果:
1001:山田太郎(総務)1002:佐藤花子(営業)1003:鈴木一郎(開発)Employeeオブジェクトを複数管理する
Section titled “Employeeオブジェクトを複数管理する”List<Employee> は、複数の社員オブジェクトをまとめて管理します。
employees ├─ Employee 1001 山田太郎 ├─ Employee 1002 佐藤花子 └─ Employee 1003 鈴木一郎後のDB接続編では、Oracle Databaseの employees 表から取得した1行ずつを Employee オブジェクトに変換し、List<Employee> に追加します。
employees表 ↓ SELECTOracleDataReader ↓ 1行ずつ読み取るEmployeeオブジェクト ↓ AddList<Employee>この章では、まず手作業で List<Employee> を作成し、複数の社員データを扱う練習をします。
メソッドでサンプルデータを作る
Section titled “メソッドでサンプルデータを作る”毎回 Main メソッドの中にサンプルデータを書くと長くなるため、サンプルデータ作成用のメソッドを用意します。
using System;using System.Collections.Generic;
class Program{ static void Main() { List<Employee> employees = CreateSampleEmployees();
foreach (Employee employee in employees) { Console.WriteLine($"{employee.EmployeeId}:{employee.EmployeeName}({employee.DepartmentName})"); } }
static List<Employee> CreateSampleEmployees() { List<Employee> employees = new List<Employee> { new Employee { EmployeeId = 1001, EmployeeName = "山田太郎", DepartmentName = "総務", Salary = 500000 }, new Employee { EmployeeId = 1002, EmployeeName = "佐藤花子", DepartmentName = "営業", Salary = 420000 }, new Employee { EmployeeId = 1003, EmployeeName = "鈴木一郎", DepartmentName = "開発", Salary = 380000 } };
return employees; }}
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } = ""; public string DepartmentName { get; set; } = ""; public int Salary { get; set; }}Listを検索する
Section titled “Listを検索する”まずは、LINQを使わずに for 文で検索してみます。
using System;using System.Collections.Generic;
class Program{ static void Main() { List<Employee> employees = CreateSampleEmployees();
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}"); } }
static List<Employee> CreateSampleEmployees() { return new List<Employee> { new Employee { EmployeeId = 1001, EmployeeName = "山田太郎", DepartmentName = "総務", Salary = 500000 }, new Employee { EmployeeId = 1002, EmployeeName = "佐藤花子", DepartmentName = "営業", Salary = 420000 }, new Employee { EmployeeId = 1003, EmployeeName = "鈴木一郎", DepartmentName = "開発", Salary = 380000 } }; }}
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } = ""; public string DepartmentName { get; set; } = ""; public int Salary { get; set; }}実行結果:
1002:佐藤花子このように、List<Employee> に対して繰り返し処理を行うことで、条件に合うデータを探せます。
ただし、検索や絞り込みを書くたびに foreach と if を書くのは少し面倒です。
そこで、LINQを使います。
12-3 LINQとは?
Section titled “12-3 LINQとは?”LINQの概要
Section titled “LINQの概要”LINQは、コレクションのデータを検索、絞り込み、並び替え、変換するための機能です。
LINQは、次のようなデータに対して使えます。
配列List<T>DBから取得したデータを入れたList<T>この章では、主に List<Employee> に対してLINQを使います。
LINQを使うには、通常、次の using を書きます。
using System.Linq;.NET 8のプロジェクトでは自動的に使える場合もありますが、研修では必要に応じて明示的に書いておきます。
Whereで絞り込む
Section titled “Whereで絞り込む”Where を使うと、条件に合うデータだけを取り出せます。
using System;using System.Collections.Generic;using System.Linq;
class Program{ static void Main() { List<Employee> employees = CreateSampleEmployees();
List<Employee> salesEmployees = employees .Where(employee => employee.DepartmentName == "営業") .ToList();
foreach (Employee employee in salesEmployees) { Console.WriteLine($"{employee.EmployeeId}:{employee.EmployeeName}({employee.DepartmentName})"); } }
static List<Employee> CreateSampleEmployees() { return new List<Employee> { new Employee { EmployeeId = 1001, EmployeeName = "山田太郎", DepartmentName = "総務", Salary = 500000 }, new Employee { EmployeeId = 1002, EmployeeName = "佐藤花子", DepartmentName = "営業", Salary = 420000 }, new Employee { EmployeeId = 1003, EmployeeName = "鈴木一郎", DepartmentName = "開発", Salary = 380000 }, new Employee { EmployeeId = 1004, EmployeeName = "田中次郎", DepartmentName = "営業", Salary = 350000 } }; }}
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } = ""; public string DepartmentName { get; set; } = ""; public int Salary { get; set; }}実行結果:
1002:佐藤花子(営業)1004:田中次郎(営業)次の部分に注目してください。
employee => employee.DepartmentName == "営業"これは ラムダ式 と呼ばれる書き方です。
この章では、まず次のように読んでください。
employee を1件ずつ取り出し、employee.DepartmentName が "営業" のものだけを選ぶWhere の中では、リストの要素が1件ずつ employee に入ります。
1件目のEmployee2件目のEmployee3件目のEmployee...その1件が条件に合うかどうかを、=> の右側で判定しています。
ToListとは
Section titled “ToListとは”Where の結果を List<Employee> として受け取りたい場合は、最後に ToList() を付けます。
List<Employee> salesEmployees = employees .Where(employee => employee.DepartmentName == "営業") .ToList();この章では、まず次のように覚えてください。
Whereで絞り込むToListでList<T>にするFirstOrDefaultで1件取得する
Section titled “FirstOrDefaultで1件取得する”条件に合う最初の1件を取得したい場合は、FirstOrDefault を使います。
using System;using System.Collections.Generic;using System.Linq;
class Program{ static void Main() { List<Employee> employees = CreateSampleEmployees();
Employee? employee = employees .FirstOrDefault(employee => employee.EmployeeId == 1002);
if (employee == null) { Console.WriteLine("社員が見つかりませんでした。"); } else { Console.WriteLine($"{employee.EmployeeId}:{employee.EmployeeName}"); } }
static List<Employee> CreateSampleEmployees() { return new List<Employee> { new Employee { EmployeeId = 1001, EmployeeName = "山田太郎", DepartmentName = "総務", Salary = 500000 }, new Employee { EmployeeId = 1002, EmployeeName = "佐藤花子", DepartmentName = "営業", Salary = 420000 }, new Employee { EmployeeId = 1003, EmployeeName = "鈴木一郎", DepartmentName = "開発", Salary = 380000 } }; }}
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } = ""; public string DepartmentName { get; set; } = ""; public int Salary { get; set; }}実行結果:
1002:佐藤花子該当するデータが見つからなかった場合、FirstOrDefault は null を返すことがあります。
そのため、戻り値を Employee? として受け取り、null チェックを行います。
Countで件数を数える
Section titled “Countで件数を数える”LINQの Count() を使うと、条件に合う件数を数えられます。
using System;using System.Collections.Generic;using System.Linq;
class Program{ static void Main() { List<Employee> employees = CreateSampleEmployees();
int salesCount = employees .Count(employee => employee.DepartmentName == "営業");
Console.WriteLine($"営業部の人数:{salesCount}人"); }
static List<Employee> CreateSampleEmployees() { return new List<Employee> { new Employee { EmployeeId = 1001, EmployeeName = "山田太郎", DepartmentName = "総務", Salary = 500000 }, new Employee { EmployeeId = 1002, EmployeeName = "佐藤花子", DepartmentName = "営業", Salary = 420000 }, new Employee { EmployeeId = 1003, EmployeeName = "鈴木一郎", DepartmentName = "開発", Salary = 380000 }, new Employee { EmployeeId = 1004, EmployeeName = "田中次郎", DepartmentName = "営業", Salary = 350000 } }; }}
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } = ""; public string DepartmentName { get; set; } = ""; public int Salary { get; set; }}実行結果:
営業部の人数:2人ここで注意したいのは、Count には2種類の使い方があることです。
| 書き方 | 種類 | 意味 |
|---|---|---|
employees.Count | プロパティ | リスト全体の件数 |
employees.Count(...) | LINQメソッド | 条件に合う件数 |
() が付くかどうかにも注目してください。
12-4 LINQで検索・絞り込みを行う
Section titled “12-4 LINQで検索・絞り込みを行う”社員名で検索する
Section titled “社員名で検索する”社員名に特定の文字が含まれるデータを検索します。
using System;using System.Collections.Generic;using System.Linq;
class Program{ static void Main() { List<Employee> employees = CreateSampleEmployees();
Console.WriteLine("検索する名前を入力してください。"); string keyword = Console.ReadLine() ?? "";
List<Employee> results = employees .Where(employee => employee.EmployeeName.Contains(keyword)) .ToList();
foreach (Employee employee in results) { Console.WriteLine($"{employee.EmployeeId}:{employee.EmployeeName}({employee.DepartmentName})"); } }
static List<Employee> CreateSampleEmployees() { return new List<Employee> { new Employee { EmployeeId = 1001, EmployeeName = "山田太郎", DepartmentName = "総務", Salary = 500000 }, new Employee { EmployeeId = 1002, EmployeeName = "佐藤花子", DepartmentName = "営業", Salary = 420000 }, new Employee { EmployeeId = 1003, EmployeeName = "鈴木一郎", DepartmentName = "開発", Salary = 380000 }, new Employee { EmployeeId = 1004, EmployeeName = "田中次郎", DepartmentName = "営業", Salary = 350000 } }; }}
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } = ""; public string DepartmentName { get; set; } = ""; public int Salary { get; set; }}実行例:
検索する名前を入力してください。田1001:山田太郎(総務)1004:田中次郎(営業)Contains を使うことで、名前の一部にキーワードが含まれているかを判定しています。
複数条件で絞り込む
Section titled “複数条件で絞り込む”条件を複数組み合わせることもできます。
次の例では、部署が営業で、給与が400000円以上の社員を取り出します。
List<Employee> results = employees .Where(employee => employee.DepartmentName == "営業" && employee.Salary >= 400000) .ToList();全体のコードは次の通りです。
using System;using System.Collections.Generic;using System.Linq;
class Program{ static void Main() { List<Employee> employees = CreateSampleEmployees();
List<Employee> results = employees .Where(employee => employee.DepartmentName == "営業" && employee.Salary >= 400000) .ToList();
foreach (Employee employee in results) { Console.WriteLine($"{employee.EmployeeName}:{employee.DepartmentName}:{employee.Salary}円"); } }
static List<Employee> CreateSampleEmployees() { return new List<Employee> { new Employee { EmployeeId = 1001, EmployeeName = "山田太郎", DepartmentName = "総務", Salary = 500000 }, new Employee { EmployeeId = 1002, EmployeeName = "佐藤花子", DepartmentName = "営業", Salary = 420000 }, new Employee { EmployeeId = 1003, EmployeeName = "鈴木一郎", DepartmentName = "開発", Salary = 380000 }, new Employee { EmployeeId = 1004, EmployeeName = "田中次郎", DepartmentName = "営業", Salary = 350000 } }; }}
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } = ""; public string DepartmentName { get; set; } = ""; public int Salary { get; set; }}実行結果:
佐藤花子:営業:420000円該当データがない場合
Section titled “該当データがない場合”検索結果が0件の場合もあります。
using System;using System.Collections.Generic;using System.Linq;
class Program{ static void Main() { List<Employee> employees = CreateSampleEmployees();
List<Employee> results = employees .Where(employee => employee.DepartmentName == "人事") .ToList();
if (results.Count == 0) { Console.WriteLine("該当する社員は見つかりませんでした。"); } else { foreach (Employee employee in results) { Console.WriteLine(employee.EmployeeName); } } }
static List<Employee> CreateSampleEmployees() { return new List<Employee> { new Employee { EmployeeId = 1001, EmployeeName = "山田太郎", DepartmentName = "総務", Salary = 500000 }, new Employee { EmployeeId = 1002, EmployeeName = "佐藤花子", DepartmentName = "営業", Salary = 420000 }, new Employee { EmployeeId = 1003, EmployeeName = "鈴木一郎", DepartmentName = "開発", Salary = 380000 } }; }}
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } = ""; public string DepartmentName { get; set; } = ""; public int Salary { get; set; }}実行結果:
該当する社員は見つかりませんでした。Webアプリやデスクトップアプリでも、検索結果が0件の場合の表示は重要です。
12-5 LINQで並び替え・集計を行う
Section titled “12-5 LINQで並び替え・集計を行う”OrderByで昇順に並び替える
Section titled “OrderByで昇順に並び替える”OrderBy を使うと、指定した項目で昇順に並び替えできます。
using System;using System.Collections.Generic;using System.Linq;
class Program{ static void Main() { List<Employee> employees = CreateSampleEmployees();
List<Employee> sortedEmployees = employees .OrderBy(employee => employee.Salary) .ToList();
foreach (Employee employee in sortedEmployees) { Console.WriteLine($"{employee.EmployeeName}:{employee.Salary}円"); } }
static List<Employee> CreateSampleEmployees() { return new List<Employee> { new Employee { EmployeeId = 1001, EmployeeName = "山田太郎", DepartmentName = "総務", Salary = 500000 }, new Employee { EmployeeId = 1002, EmployeeName = "佐藤花子", DepartmentName = "営業", Salary = 420000 }, new Employee { EmployeeId = 1003, EmployeeName = "鈴木一郎", DepartmentName = "開発", Salary = 380000 } }; }}
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } = ""; public string DepartmentName { get; set; } = ""; public int Salary { get; set; }}実行結果:
鈴木一郎:380000円佐藤花子:420000円山田太郎:500000円OrderByDescendingで降順に並び替える
Section titled “OrderByDescendingで降順に並び替える”降順に並び替えたい場合は、OrderByDescending を使います。
List<Employee> sortedEmployees = employees .OrderByDescending(employee => employee.Salary) .ToList();給与が高い順に表示したい場合などに使えます。
Sumで合計を求める
Section titled “Sumで合計を求める”Sum を使うと、指定した数値の合計を求められます。
using System;using System.Collections.Generic;using System.Linq;
class Program{ static void Main() { List<Employee> employees = CreateSampleEmployees();
int totalSalary = employees.Sum(employee => employee.Salary);
Console.WriteLine($"給与合計:{totalSalary}円"); }
static List<Employee> CreateSampleEmployees() { return new List<Employee> { new Employee { EmployeeId = 1001, EmployeeName = "山田太郎", DepartmentName = "総務", Salary = 500000 }, new Employee { EmployeeId = 1002, EmployeeName = "佐藤花子", DepartmentName = "営業", Salary = 420000 }, new Employee { EmployeeId = 1003, EmployeeName = "鈴木一郎", DepartmentName = "開発", Salary = 380000 } }; }}
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } = ""; public string DepartmentName { get; set; } = ""; public int Salary { get; set; }}実行結果:
給与合計:1300000円Averageで平均を求める
Section titled “Averageで平均を求める”Average を使うと、平均を求められます。
double averageSalary = employees.Average(employee => employee.Salary);
Console.WriteLine($"平均給与:{averageSalary}円");表示用に丸めたい場合は、Math.Round と組み合わせてもよいです。
double roundedAverage = Math.Round(averageSalary, 1);Selectで必要な項目だけを取り出す
Section titled “Selectで必要な項目だけを取り出す”Select を使うと、リストの各要素から必要な情報だけを取り出せます。
次の例では、社員名だけを取り出します。
using System;using System.Collections.Generic;using System.Linq;
class Program{ static void Main() { List<Employee> employees = CreateSampleEmployees();
List<string> employeeNames = employees .Select(employee => employee.EmployeeName) .ToList();
foreach (string name in employeeNames) { Console.WriteLine(name); } }
static List<Employee> CreateSampleEmployees() { return new List<Employee> { new Employee { EmployeeId = 1001, EmployeeName = "山田太郎", DepartmentName = "総務", Salary = 500000 }, new Employee { EmployeeId = 1002, EmployeeName = "佐藤花子", DepartmentName = "営業", Salary = 420000 }, new Employee { EmployeeId = 1003, EmployeeName = "鈴木一郎", DepartmentName = "開発", Salary = 380000 } }; }}
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } = ""; public string DepartmentName { get; set; } = ""; public int Salary { get; set; }}実行結果:
山田太郎佐藤花子鈴木一郎匿名型で必要な形に変換する
Section titled “匿名型で必要な形に変換する”Select では、必要な項目だけを持つ新しい形のデータを作ることもできます。
var displayEmployees = employees .Select(employee => new { employee.EmployeeId, employee.EmployeeName }) .ToList();この new { ... } は匿名型と呼ばれます。
この章では深く扱いませんが、画面表示用に必要な項目だけを取り出す考え方は、後のWebアプリやデスクトップアプリでも重要です。
12-6 DB連携に向けたListの考え方
Section titled “12-6 DB連携に向けたListの考え方”DBの1行を1つのオブジェクトとして扱う
Section titled “DBの1行を1つのオブジェクトとして扱う”後の章では、Oracle Databaseの employees 表からデータを取得します。
イメージは次の通りです。
employees表
employee_id | employee_name | department_id------------|---------------|--------------1001 | 山田二郎 | 11002 | 佐藤昭夫 | 21003 | 山口洋子 | 3C#側では、1行分を Employee オブジェクトとして扱います。
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } = ""; public int DepartmentId { get; set; }}複数行の結果は、List<Employee> として扱います。
List<Employee> employees = new List<Employee>();DBから取得した後の処理を想定する
Section titled “DBから取得した後の処理を想定する”この章では、まだDBには接続しません。
しかし、次のように手作業で作成した List<Employee> は、DBから取得した結果と同じように扱えます。
List<Employee> employees = new List<Employee>{ new Employee { EmployeeId = 1001, EmployeeName = "山田二郎", DepartmentId = 1 }, new Employee { EmployeeId = 1002, EmployeeName = "佐藤昭夫", DepartmentId = 2 }, new Employee { EmployeeId = 1003, EmployeeName = "山口洋子", DepartmentId = 3 }};このリストに対して、LINQで検索できます。
Employee? employee = employees .FirstOrDefault(employee => employee.EmployeeId == 1002);部署で絞り込むこともできます。
List<Employee> departmentEmployees = employees .Where(employee => employee.DepartmentId == 1) .ToList();つまり、DB接続編に入る前に、List<Employee> と LINQ に慣れておくことが重要です。
Webアプリ・デスクトップアプリでの利用イメージ
Section titled “Webアプリ・デスクトップアプリでの利用イメージ”Webアプリでは、ControllerからViewへ List<Employee> を渡すことがあります。
Controller ↓List<Employee> ↓Viewで一覧表示デスクトップアプリでは、List<Employee> をDataGridViewに表示することがあります。
Oracle DB ↓List<Employee> ↓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 の検索結果が想定と違う | 大文字小文字や空白の違い | 必要に応じて Trim や ToLower を使う |
| LINQを使いすぎて読みにくい | 1行に詰め込みすぎている | 改行して読みやすく書く |
学んだことチェック
Section titled “学んだことチェック”次の項目について、自分で説明できるか確認してください。
-
List<T>とは何かを説明できる - 配列と
List<T>の違いを説明できる -
List<int>を作成できる -
List<string>を作成できる -
List<Employee>を作成できる -
Addを使ってリストに要素を追加できる -
Countでリストの件数を取得できる -
foreachでリストの要素を順番に処理できる - LINQとは何かをおおまかに説明できる
-
Whereを使って絞り込みができる -
FirstOrDefaultを使って1件取得できる -
FirstOrDefaultの結果がnullになる可能性を説明できる -
OrderByで並び替えできる -
SumやAverageで集計できる -
Selectで必要な項目だけを取り出せる - DBから取得した複数件のデータを
List<Employee>として扱うイメージを説明できる
研修の進め方によっては、隣の人またはチーム内で説明確認を行います。
次の内容を、自分の言葉で説明してください。
- 配列と
List<T>の違いは何ですか。 List<Employee>は何を表していますか。employees.Countとemployees.Count(...)の違いは何ですか。Whereはどのようなときに使いますか。FirstOrDefaultの結果を使う前に注意することは何ですか。- DBから取得した複数件の社員データを、C#ではどのように扱う予定ですか。
説明するときは、完全な答えでなくても構いません。
自分の言葉で説明しようとすることが大切です。
この章の演習課題に取り組みます。
制限時間は 75分 です。
時間内にすべて完成しなくても構いません。
できたところまでを保存し、Gitに提出してください。
まずは、全員が必須課題に取り組んでください。
課題12-1 Listで点数を管理する
Section titled “課題12-1 Listで点数を管理する”List<int> を使って、次の点数を管理し、すべて表示してください。
80, 75, 90, 68, 85実行結果例:
8075906885条件:
List<int>を使うAddで点数を追加するforeachで表示する
課題12-2 Listで部署一覧を管理する
Section titled “課題12-2 Listで部署一覧を管理する”List<string> を使って、次の部署名を管理し、件数と一覧を表示してください。
総務, 営業, 開発, マーケティング実行結果例:
部署数:4総務営業開発マーケティング条件:
List<string>を使うCountを使うforeachを使う
課題12-3 Listを作成する
Section titled “課題12-3 Listを作成する”Employee クラスを作成し、List<Employee> で3人分の社員情報を管理してください。
プロパティ:
| プロパティ名 | 型 |
|---|---|
EmployeeId | int |
EmployeeName | string |
DepartmentName | string |
実行結果例:
1001:山田太郎(総務)1002:佐藤花子(営業)1003:鈴木一郎(開発)条件:
Employeeクラスを作成するList<Employee>を使う- オブジェクト初期化子を使う
foreachで表示する
課題12-4 Whereで部署検索する
Section titled “課題12-4 Whereで部署検索する”課題12-3の List<Employee> を使い、部署名が "営業" の社員だけを表示してください。
実行結果例:
1002:佐藤花子(営業)条件:
using System.Linq;を書くWhereを使うToList()を使う
課題12-5 FirstOrDefaultで社員ID検索する
Section titled “課題12-5 FirstOrDefaultで社員ID検索する”社員IDを指定し、該当する社員を1件取得してください。
実行結果例:
1002:佐藤花子該当しない場合の実行結果例:
社員が見つかりませんでした。条件:
FirstOrDefaultを使う- 結果を
Employee?で受け取る nullチェックを行う
必須課題が終わった人は、発展課題に取り組んでください。
課題12-6 社員名の部分一致検索
Section titled “課題12-6 社員名の部分一致検索”社員名に入力されたキーワードが含まれる社員だけを表示してください。
実行例:
検索キーワードを入力してください。田1001:山田太郎(総務)1004:田中次郎(営業)条件:
Console.ReadLineを使うContainsを使うWhereを使う- 検索結果が0件の場合はメッセージを表示する
課題12-7 給与の高い順に表示する
Section titled “課題12-7 給与の高い順に表示する”Employee クラスに Salary プロパティを追加し、給与の高い順に社員を表示してください。
実行結果例:
山田太郎:500000円佐藤花子:420000円鈴木一郎:380000円条件:
Salaryプロパティを追加するOrderByDescendingを使うToList()を使う
課題12-8 給与合計と平均を求める
Section titled “課題12-8 給与合計と平均を求める”List<Employee> から給与合計と平均給与を求めて表示してください。
実行結果例:
給与合計:1300000円平均給与:433333.3333333333円条件:
Sumを使うAverageを使う- 必要に応じて
Math.Roundを使ってもよい
課題12-9 部署と給与で複数条件検索する
Section titled “課題12-9 部署と給与で複数条件検索する”部署が "営業" で、給与が 400000 円以上の社員だけを表示してください。
実行結果例:
佐藤花子:営業:420000円条件:
Whereを使う&&を使って複数条件を指定する
課題12-10 社員名だけを取り出す
Section titled “課題12-10 社員名だけを取り出す”Select を使って、社員名だけの List<string> を作成し、表示してください。
実行結果例:
山田太郎佐藤花子鈴木一郎条件:
Selectを使うList<string>として受け取るforeachで表示する
課題12-11 DBデータを想定したEmployeeリストを作る
Section titled “課題12-11 DBデータを想定したEmployeeリストを作る”後のDB接続編を想定し、次のプロパティを持つ Employee クラスを作成してください。
| プロパティ名 | 型 |
|---|---|
EmployeeId | int |
EmployeeName | string |
Yomi | string? |
ManagerId | int? |
HireDate | DateTime |
Salary | decimal? |
Commission | decimal? |
DepartmentId | int |
3件分のサンプルデータを List<Employee> に入れ、次のように表示してください。
実行結果例:
1001:山田二郎:部署ID 1:給与 5000001020:内田雄介:部署ID 1:給与 2000001021:高田明:部署ID 1:給与 200000条件:
List<Employee>を使うstring?、int?、decimal?を使う- 給与が
nullの場合は"未設定"と表示する - 後のOracle DB接続編の準備として考える
Gitへの提出
Section titled “Gitへの提出”課題が終わったら、できたところまでをGitに提出します。
まず、現在の状態を確認します。
git status変更されたファイルを追加します。
git add .コミットします。
git commit -m "Chapter12 ListとLINQ"ファイルサーバー上のリポジトリへpushします。
git pushGitの操作でエラーが出た場合は、自己判断で同じ操作を繰り返さず、講師に確認してください。
提出前チェックリスト
Section titled “提出前チェックリスト”提出前に、次の項目を確認してください。
-
List<int>を使えている -
List<string>を使えている -
List<Employee>を使えている -
Addで要素を追加できている -
Countで件数を取得できている -
foreachでリストを処理できている -
Whereで絞り込みができている -
FirstOrDefaultで1件取得できている -
FirstOrDefaultの結果をnullチェックしている -
OrderByまたはOrderByDescendingを使えている -
SumまたはAverageを使えている -
Selectを使えている - インデントが整っている
- Gitにcommitしている
- Gitにpushしている
この章のまとめ
Section titled “この章のまとめ”この章では、List<T> と LINQ について学習しました。
この章で学んだ主な内容は次の通りです。
List<T>は、同じ型のデータを複数管理するためのコレクションであるTには、int、string、Employeeなどの型を指定するList<T>は、配列よりも要素の追加や削除がしやすいAddを使うと、リストに要素を追加できるCountを使うと、リストの件数を取得できるforeachを使うと、リストの要素を順番に処理できるList<Employee>を使うと、複数の社員オブジェクトをまとめて扱える- LINQを使うと、検索、絞り込み、並び替え、集計ができる
Whereは、条件に合うデータを絞り込むFirstOrDefaultは、条件に合う最初の1件を取得するFirstOrDefaultの結果はnullになる可能性があるOrderBy、OrderByDescendingで並び替えできるSum、Averageで集計できるSelectで必要な項目だけを取り出せる- 後のDB接続編では、DBから取得した複数件のデータを
List<Employee>として扱う
次章では、継承とポリモーフィズム を学習します。
クラス同士の共通点をまとめたり、同じ呼び出し方で異なる動きをさせたりする考え方を学びます。
現場の既存コードを読むうえでも重要な考え方です。