第10章 クラスについて掘り下げる
この章の目的
Section titled “この章の目的”この章では、第 7 章から学習してきたクラスについて、より実践的な使い方を学習します。
第 7 章では、クラスとオブジェクトの基本、プロパティ、メソッドを学びました。
第 8 章では static、第 9 章では .NET の標準クラスを使ってきました。
この章では、自分で作るクラスを 使いやすく、安全に するための機能を扱います。
さまざまな型を返すメソッド(int、bool、自作クラスなど)メソッドのオーバーロード(同名メソッドの複数定義)省略可能な引数コンストラクター(オブジェクト作成時の初期化)プロパティの高度な使い方(get/set、読み取り専用、private set)これらは、オブジェクト指向プログラミングで重要な カプセル化(オブジェクトの中身を守る考え方)につながります。 この章では難しく考えすぎず、まずは次のように理解することを目指します。
オブジェクトを作るときに必要な値を確実に設定する不正な値が入らないようにする外から変更してよい値と、変更させたくない値を分けるこの章でできるようになること
Section titled “この章でできるようになること”この章を終えると、次のことができるようになります。
- メソッドが
int・string・bool・自作クラスなど、さまざまな型を返せることを説明できる - メソッドのオーバーロードを定義できる
- 省略可能な引数を持つメソッドを定義できる
- 名前付き引数で呼び出せる
- 引数付きコンストラクターを定義できる
- コンストラクターをオーバーロードできる
this(...)で別のコンストラクターを呼び出せるget/setを明示したプロパティを書けるvalueの意味を説明できる- 読み取り専用プロパティを作成できる
private setで外部からの変更を制限できる- プロパティに初期値を設定できる
- アクセス修飾子(
public/private)の意味と使い分けを説明できる - カプセル化が目指す 3 つのこと(状態を隠す / 操作を限定する / 不正な状態を作らせない)を説明できる
本章で使用する環境
Section titled “本章で使用する環境”| 項目 | 内容 |
|---|---|
| 開発環境 | Visual Studio 2022 |
| プロジェクト種類 | コンソール アプリ |
| 対象フレームワーク | .NET 8 |
| ソリューション名 | Chapter10 |
| プロジェクト名 | Ch10_ClassDetails |
csproj の Nullable は disable に変更してください
プロジェクト作成後、
Ch10_ClassDetails.csprojを開き、<Nullable>disable</Nullable>に変更してください。詳しい手順は、第 1 章「1-1 プロジェクトを作成する」を参照してください。
作業前チェック
Section titled “作業前チェック”作業を始める前に、次の内容を確認してください。
- クラスを別ファイル(
Employee.csなど)に作成できる(第 7 章) - プロパティ・メソッドを定義できる
- 戻り値のあるメソッド・引数のあるメソッドを書ける
- 静的メソッドとインスタンスメソッドの違いを説明できる
- 第 9 章の内容を Git に提出済みである
10-1 メソッドはどんな型でも返せる
Section titled “10-1 メソッドはどんな型でも返せる”戻り値の型は自由に選べる
Section titled “戻り値の型は自由に選べる”メソッドは、結果を呼び出し元に返すことができます。これを 戻り値 といいます。
戻り値の型は、int・string・bool などの基本型だけでなく、自作クラスの型 も指定できます。
| 戻り値の型 | 例 |
|---|---|
int | 計算結果(金額、点数) |
string | 表示用文字列 |
bool | 判定結果(合格か否か) |
| 自作クラス | オブジェクトそのもの |
int や string を返すメソッドは、これまでにも書いてきました。この章では、特に bool を返すメソッド と 自作クラスを返すメソッド を中心に見ていきます。あわせて、int / string を返すメソッドの より実用的な書き方(条件で結果を切り替える / 計算結果をまとめる)もバリエーションとして取り上げます。
bool 型を返すメソッド
Section titled “bool 型を返すメソッド”条件判定の結果を bool で返すメソッドはよく使います。
次の例では、点数が合格点(60 点)以上かどうかを判定します。
TestResult.cs:
namespace Ch10_ClassDetails;
class TestResult{ public string EmployeeName { get; set; } public int Score { get; set; }
public bool IsPassed() { return Score >= 60; }}Program.cs:
namespace Ch10_ClassDetails;
internal class Program{ static void Main(string[] args) { TestResult result = new TestResult { EmployeeName = "山田二郎", Score = 75 };
if (result.IsPassed()) { Console.WriteLine($"{result.EmployeeName}:合格"); } else { Console.WriteLine($"{result.EmployeeName}:不合格"); } }}実行結果:
山田二郎:合格IsPassed メソッドは Score >= 60 の結果(true または false)をそのまま返しています。
bool を返すメソッドは、if 文の条件としてそのまま使えます。
補足:
Is〇〇、Has〇〇、Can〇〇
boolを返すメソッドの名前は、Is〇〇(〇〇か?)、Has〇〇(〇〇を持つか?)、Can〇〇(〇〇できるか?)のような形にすると、if文で読みやすくなります。
string 型を返すメソッド
Section titled “string 型を返すメソッド”string 型は、これまでも何度も返してきました。ここでは、条件に応じて返す文字列を切り替える パターンを見ます。
次の例では、点数から評価ランク("A"/"B"/"C"/"D")を返します。
Grader.cs:
namespace Ch10_ClassDetails;
class Grader{ public string GetGrade(int score) { if (score >= 80) return "A"; if (score >= 60) return "B"; if (score >= 40) return "C"; return "D"; }}Program.cs:
Grader grader = new Grader();
Console.WriteLine($"85点:{grader.GetGrade(85)}");Console.WriteLine($"55点:{grader.GetGrade(55)}");Console.WriteLine($"30点:{grader.GetGrade(30)}");実行結果:
85点:A55点:B30点:Dポイント:
ifの条件に当てはまった瞬間にreturnしているreturnが実行されると その時点でメソッドは終わる ため、それ以降のifは判定されない- 最後の
return "D";は、上のどれにも当てはまらなかったときの「それ以外」を表す
このように「条件で結果が変わる文字列を返す」のは、表示用文字列やラベル取得などで頻出のパターンです。
計算結果(int)を返すメソッド
Section titled “計算結果(int)を返すメソッド”数値計算の結果を int で返すメソッドも、業務でよく登場します。
次の例では、基本給と手当の合計、賞与(月給 × か月数)を返します。
SalaryCalculator.cs:
namespace Ch10_ClassDetails;
class SalaryCalculator{ public int GetTotalSalary(int baseSalary, int allowance) { return baseSalary + allowance; }
public int GetBonus(int monthlySalary, int months) { return monthlySalary * months; }}Program.cs:
SalaryCalculator calc = new SalaryCalculator();
int total = calc.GetTotalSalary(300000, 50000);int bonus = calc.GetBonus(300000, 2);
Console.WriteLine($"総支給額:{total}円");Console.WriteLine($"賞与(2か月分):{bonus}円");実行結果:
総支給額:350000円賞与(2か月分):600000円メソッドが「入力(引数)から 出力(戻り値)を作る装置」になっているのが分かります。 同じ計算を 2 か所以上で使うときは、毎回式を書くよりメソッドにまとめると、読みやすく、間違いも減ります。
自作クラスを返すメソッド
Section titled “自作クラスを返すメソッド”メソッドは、自作クラスのオブジェクト を返すこともできます。 たとえば「サンプル用の社員を作って返す」というメソッドを用意できます。
補足:この節で使う
Employeeクラスについて次のサンプルは、第 7 章で作成した
Employeeクラスをそのまま使います。 同じプロジェクトにEmployee.csを追加し、次の 3 つのプロパティを定義してください。Employee.cs namespace Ch10_ClassDetails;class Employee{public int EmployeeId { get; set; }public string EmployeeName { get; set; }public string DepartmentName { get; set; }}
EmployeeFactory.csとEmployee.csのnamespaceを同じCh10_ClassDetailsに揃えると、usingを書かなくても互いに参照できます。
EmployeeFactory.cs:
namespace Ch10_ClassDetails;
static class EmployeeFactory{ public static Employee CreateSampleEmployee() { return new Employee { EmployeeId = 1001, EmployeeName = "山田二郎", DepartmentName = "総務" }; }}Program.cs:
namespace Ch10_ClassDetails;
internal class Program{ static void Main(string[] args) { Employee employee = EmployeeFactory.CreateSampleEmployee();
Console.WriteLine($"{employee.EmployeeId}:{employee.EmployeeName}({employee.DepartmentName})"); }}実行結果:
1001:山田二郎(総務)戻り値の型に Employee と書いています。
public static Employee CreateSampleEmployee()このメソッドは、Employee クラスのオブジェクトを返します。
この書き方は、後の章で扱うリストやコレクションでもよく使う形です。
戻り値の型と return の型は一致させる
Section titled “戻り値の型と return の型は一致させる”メソッドの戻り値の型と、return で返す値の型は、必ず一致 している必要があります。
public int GetAmount(){ return "900"; // ← エラー:int を返す約束なのに string を返している}戻り値の型が int なら int を、string なら string を、Employee なら Employee を返します。
10-2 メソッドのオーバーロード
Section titled “10-2 メソッドのオーバーロード”オーバーロードとは
Section titled “オーバーロードとは”オーバーロード とは、同じ名前で引数の異なるメソッドを複数定義する ことです。
これまでも、知らないうちにオーバーロードを使ってきました。
Console.WriteLine("こんにちは");Console.WriteLine(100);Console.WriteLine(true);同じ WriteLine という名前でも、文字列・数値・真偽値など、さまざまな型を渡せます。
これは Console.WriteLine が 多数のオーバーロード を持っているためです。
自分でオーバーロードを作る
Section titled “自分でオーバーロードを作る”社員検索用の Search メソッドを、社員番号でも氏名でも呼べるようにします。
EmployeeSearchService.cs:
namespace Ch10_ClassDetails;
class EmployeeSearchService{ public void Search(int employeeId) { Console.WriteLine($"社員番号 {employeeId} で検索します。"); }
public void Search(string employeeName) { Console.WriteLine($"氏名 {employeeName} で検索します。"); }}Program.cs:
namespace Ch10_ClassDetails;
internal class Program{ static void Main(string[] args) { EmployeeSearchService service = new EmployeeSearchService();
service.Search(1001); service.Search("山田"); }}実行結果:
社員番号 1001 で検索します。氏名 山田 で検索します。どちらも Search という名前ですが、引数の 型 が違うので、C# は呼び出し時にどちらを使うか判断できます。
引数の数が違うオーバーロード
Section titled “引数の数が違うオーバーロード”引数の 数 が違ってもオーバーロードできます。
class ReportPrinter{ public void Print(string title) { Console.WriteLine($"タイトル:{title}"); }
public void Print(string title, string date) { Console.WriteLine($"タイトル:{title}"); Console.WriteLine($"日付:{date}"); }}| 呼び出し | 動作 |
|---|---|
printer.Print("売上レポート") | タイトルだけ表示 |
printer.Print("売上レポート", "2026/05/18") | タイトルと日付を表示 |
3 つ以上のオーバーロード
Section titled “3 つ以上のオーバーロード”オーバーロードは 3 つ以上でも定義できます。 業務コードでは、引数の組み合わせを変えた 3〜5 個のオーバーロードを持つメソッドは珍しくありません。
次の PriceFormatter は、価格表示のオーバーロードを 3 つ持ちます。
PriceFormatter.cs:
namespace Ch10_ClassDetails;
class PriceFormatter{ public string Format(int price) { return $"{price}円"; }
public string Format(int price, string label) { return $"{label}:{price}円"; }
public string Format(int price, string label, int quantity) { return $"{label}:{price}円 × {quantity}個 = {price * quantity}円"; }}呼び出し:
PriceFormatter formatter = new PriceFormatter();
Console.WriteLine(formatter.Format(180));Console.WriteLine(formatter.Format(180, "ノート"));Console.WriteLine(formatter.Format(180, "ノート", 5));実行結果:
180円ノート:180円ノート:180円 × 5個 = 900円引数を 「少しずつ詳しくしていく」 発想は、Console.WriteLine(値だけ表示 → 書式付き表示 → 複数の値を埋め込み表示)など、.NET の標準クラスでもよく見られるパターンです。
補足:オーバーロードと省略可能な引数、どちらを使う?
上の
PriceFormatterは、次に学ぶ「省略可能な引数」(10-3)でも書けます。public string Format(int price, string label = "", int quantity = 1) { ... }どちらを使うかは状況によります。引数の組み合わせごとに処理がはっきり違う場合はオーバーロード、既定値で動かしたいだけの場合は省略可能な引数 が読みやすくなります。 迷ったときは、章末「よくあるつまずき」や配属先のコード規約を参考にしてください。
オーバーロードできない例
Section titled “オーバーロードできない例”戻り値の型だけが違うメソッド は、オーバーロードできません。
public int GetValue(){ return 100;}
public string GetValue() // ← エラー{ return "100";}呼び出すときに引数だけでは区別できないためです。
| オーバーロードできる条件 | 例 |
|---|---|
| 引数の 型 が違う | Search(int) と Search(string) |
| 引数の 数 が違う | Print(string) と Print(string, string) |
| 戻り値の型だけが違う | ❌ できない |
10-3 省略可能な引数
Section titled “10-3 省略可能な引数”既定値を持つ引数
Section titled “既定値を持つ引数”メソッドの引数に 既定値 を設定しておくと、呼び出し時に省略できます。
class GreetingService{ public string CreateMessage(string employeeName, string title = "さん") { return $"{employeeName}{title}、こんにちは。"; }}Program.cs:
namespace Ch10_ClassDetails;
internal class Program{ static void Main(string[] args) { GreetingService service = new GreetingService();
Console.WriteLine(service.CreateMessage("山田二郎")); Console.WriteLine(service.CreateMessage("田中浩介", "先生")); }}実行結果:
山田二郎さん、こんにちは。田中浩介先生、こんにちは。title を省略すると、既定値の "さん" が使われます。
省略可能な引数は後ろに置く
Section titled “省略可能な引数は後ろに置く”省略可能な引数は、必ず通常の引数の 後ろ に書きます。
// OKpublic int GetPayment(int unitPrice, int quantity, int discount = 0)
// エラーpublic int GetPayment(int discount = 0, int unitPrice, int quantity)省略可能な引数を前に置くと、どの引数を省略したのか判断できなくなるためです。
名前付き引数
Section titled “名前付き引数”省略可能な引数を組み合わせて、名前付き引数 で呼び出すこともできます。
class ReportService{ public void PrintReport(string title, bool includeDate = false) { Console.WriteLine($"タイトル:{title}");
if (includeDate) { Console.WriteLine($"作成日:{DateTime.Today.ToString("yyyy/MM/dd")}"); } }}呼び出し例:
service.PrintReport("売上レポート", includeDate: true);引数名: 値 の形で書くと、何の引数なのかが読みやすくなります。
省略可能な引数が増えても、どれを指定したのかが明確になります。
複数の省略可能な引数を組み合わせる
Section titled “複数の省略可能な引数を組み合わせる”省略可能な引数は、1 つだけでなく 複数並べる こともできます。 次の例では、価格表示に関する 3 つの引数(通貨単位・税込表示するか・税率)を省略可能にしています。
ProductPrinter.cs:
namespace Ch10_ClassDetails;
class ProductPrinter{ public void Print( string productName, int price, string currency = "円", bool includeTax = false, decimal taxRate = 1.10m) { int displayPrice = price; if (includeTax) { displayPrice = (int)(price * taxRate); }
Console.WriteLine($"{productName}:{displayPrice}{currency}"); }}呼び出し例:
ProductPrinter printer = new ProductPrinter();
printer.Print("ノート", 180);printer.Print("ノート", 180, includeTax: true);printer.Print("ノート", 180, currency: "JPY", includeTax: true);実行結果:
ノート:180円ノート:198円ノート:198JPYポイント:
- 既定値はそれぞれ独立して使われる(指定した引数だけ既定値から変わる)
- 名前付き引数を使うと、途中の引数を飛ばして指定 できる(2 番目の例で
currencyを飛ばしてincludeTaxをtrueに) - 名前付き引数を組み合わせると、引数の順序を覚えていなくても呼べる
引数が増えてきたら、名前付き引数で呼び出す側を読みやすくする のが定番です。
章前半ここまで、後半は次の節から
10-1〜10-3 で、メソッドの柔軟な書き方(戻り値の型・オーバーロード・省略可能な引数)を一通り見ました。 後半(10-4〜10-5)では、クラスの状態管理(コンストラクター、プロパティの高度な使い方、アクセス修飾子、カプセル化)に進みます。後半は本章の主役で、覚えることが多めです。
10-4 コンストラクターを使いこなす
Section titled “10-4 コンストラクターを使いこなす”コンストラクターとは
Section titled “コンストラクターとは”コンストラクター とは、new でオブジェクトを作成するときに 自動的に呼び出される 特別なメソッドです。
Employee employee = new Employee(); // ← この瞬間に Employee のコンストラクターが呼ばれる主な目的は次のとおりです。
オブジェクト作成時に初期値を設定する必要な値を必ず受け取る不完全な状態のオブジェクトを作らせない引数付きコンストラクター
Section titled “引数付きコンストラクター”第 7 章では、オブジェクトを作ってからプロパティに 1 つずつ値を代入していました。 コンストラクターを使うと、作成と同時に必要な値を渡せます。
Employee.cs:
namespace Ch10_ClassDetails;
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } public string DepartmentName { get; set; }
public Employee(int employeeId, string employeeName, string departmentName) { EmployeeId = employeeId; EmployeeName = employeeName; DepartmentName = departmentName; }
public string GetDisplayText() { return $"{EmployeeId}:{EmployeeName}({DepartmentName})"; }}Program.cs:
namespace Ch10_ClassDetails;
internal class Program{ static void Main(string[] args) { Employee employee = new Employee(1001, "山田二郎", "総務");
Console.WriteLine(employee.GetDisplayText()); }}実行結果:
1001:山田二郎(総務)コンストラクターの書き方の特徴
Section titled “コンストラクターの書き方の特徴”public Employee(int employeeId, string employeeName, string departmentName){ EmployeeId = employeeId; EmployeeName = employeeName; DepartmentName = departmentName;}通常のメソッドと違う点は 2 つです。
| 項目 | 通常メソッド | コンストラクター |
|---|---|---|
| メソッド名 | 自由 | クラス名と同じ |
| 戻り値の型 | 必要(void 含む) | 書かない |
コンストラクターを使うメリット
Section titled “コンストラクターを使うメリット”コンストラクターを使うと、オブジェクトが 作成された瞬間に必要な情報が揃っている 状態になります。
| 方式 | 書き方 | リスク |
|---|---|---|
| コンストラクターなし | new してから 1 つずつ代入 | 代入し忘れに気付きにくい |
| コンストラクターあり | new Employee(1001, "山田二郎", "総務") | 引数を渡さないとコンパイルエラーになる |
コンストラクターのオーバーロード
Section titled “コンストラクターのオーバーロード”コンストラクターも、メソッドと同じようにオーバーロードできます。
namespace Ch10_ClassDetails;
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } public string DepartmentName { get; set; }
public Employee() { EmployeeId = 0; EmployeeName = "未設定"; DepartmentName = "未所属"; }
public Employee(int employeeId, string employeeName, string departmentName) { EmployeeId = employeeId; EmployeeName = employeeName; DepartmentName = departmentName; }
public string GetDisplayText() { return $"{EmployeeId}:{EmployeeName}({DepartmentName})"; }}呼び出し:
Employee employee1 = new Employee();Employee employee2 = new Employee(1001, "山田二郎", "総務");
Console.WriteLine(employee1.GetDisplayText());Console.WriteLine(employee2.GetDisplayText());実行結果:
0:未設定(未所属)1001:山田二郎(総務)this で別のコンストラクターを呼び出す
Section titled “this で別のコンストラクターを呼び出す”複数のコンストラクターで同じ処理を書くと重複します。
: this(...) で 別のコンストラクターを呼び出す と、初期化処理を 1 か所にまとめられます。
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } public string DepartmentName { get; set; }
public Employee() : this(0, "未設定", "未所属") { }
public Employee(int employeeId, string employeeName, string departmentName) { EmployeeId = employeeId; EmployeeName = employeeName; DepartmentName = departmentName; }}Employee() の : this(0, "未設定", "未所属") は、「Employee(int, string, string) を呼び出してから自分の中身を実行する」という意味です。
コンストラクターで値を補正する
Section titled “コンストラクターで値を補正する”コンストラクターの中で、簡単な値チェックを入れることもできます。
Product.cs:
namespace Ch10_ClassDetails;
class Product{ public string ProductName { get; set; } public int Price { get; set; }
public Product(string productName, int price) { ProductName = productName;
if (price < 0) { Price = 0; } else { Price = price; } }}呼び出し:
Product product = new Product("ノート", -180);
Console.WriteLine($"{product.ProductName}:{product.Price}円");実行結果:
ノート:0円不正な値(-180)が補正され、0 が代入されました。
本格的なエラー通知は後の例外処理章で扱います。
コンストラクター内で別のメソッドを呼ぶ
Section titled “コンストラクター内で別のメソッドを呼ぶ”コンストラクターの中身が長くなってきたら、処理を別メソッドに切り出す とすっきりします。
次の例では、値を「最小値〜最大値の範囲に収める」処理を Clamp メソッドに切り出しています。
OrderQuantity.cs:
namespace Ch10_ClassDetails;
class OrderQuantity{ public int Quantity { get; set; } public int Stock { get; set; }
public OrderQuantity(int quantity, int stock) { Quantity = Clamp(quantity, 1, 99); Stock = Clamp(stock, 0, 9999); }
private int Clamp(int value, int min, int max) { if (value < min) return min; if (value > max) return max; return value; }}呼び出し:
OrderQuantity order = new OrderQuantity(150, -5);
Console.WriteLine($"数量:{order.Quantity}、在庫:{order.Stock}");実行結果:
数量:99、在庫:0ポイント:
- 同じ「範囲補正」ロジックを 1 か所にまとめて使い回す ので、書き間違いを減らせる
Clampはprivateを付けて、クラスの内部でだけ使う道具 であることを表す(privateの詳しい意味は 10-5「アクセス修飾子」で学びます)- コンストラクターが「入口チェック」、
privateメソッドが「内部の道具」というように、役割分担ができる
補足:引数 1 つだけのシンプルなコンストラクター
コンストラクターは、プロパティが 1 つだけのシンプルなクラスでも有用です。
class TrainingDay{public int DayNumber { get; set; }public TrainingDay(int dayNumber){DayNumber = dayNumber;}}
new TrainingDay(3)のように、作成と同時に必要な値を 1 つだけ受け取る 形です。 引数の数は 必要なだけ で構いません(0 個・1 個・3 個・5 個など、業務の都合に合わせて)。
ここまでの流れと、次の節
10-4 で扱ったコンストラクターは、オブジェクトを 正しく作る ための道具でした。 次の 10-5 では、作った後の プロパティの読み書きをより細かく制御する 道具を見ていきます。 「10-4 = 作る側 / 10-5 = 守る側」と覚えると、章後半の全体像が掴みやすくなります。
10-5 プロパティの高度な使い方
Section titled “10-5 プロパティの高度な使い方”この節では、プロパティを 「ただの値置き場」から「賢い窓口」に進化させる方法 を学びます。次の流れで進みます。
get / set を明示する → 値の取得・代入に処理を加えるバッキングフィールドと value → 内部の値の保管場所読み取り専用プロパティ → 外から代入できないアクセス修飾子 → 「どこから使えるか」を決めるprivate set → 読み取りは外に開き、代入はクラス内だけに閉じるプロパティの初期化 → オブジェクト作成直後の値カプセル化 → ここまでの道具をまとめた考え方最後の「カプセル化」で、これらの道具がどう組み合わさるかを整理します。
自動実装プロパティのおさらい
Section titled “自動実装プロパティのおさらい”これまで使ってきたプロパティは 自動実装プロパティ と呼ばれます。
public string EmployeeName { get; set; }これは「値の取得(get)と代入(set)を自動で用意する」書き方です。
単に値を保持するだけなら、これで十分です。
get / set を明示して書く
Section titled “get / set を明示して書く”値の取得・代入時に 処理を加えたい 場合は、get と set を明示します。
次の例では、点数が 0 未満なら 0、100 超なら 100 に補正します。
TestScore.cs:
namespace Ch10_ClassDetails;
class TestScore{ private int _score;
public int Score { get { return _score; } set { if (value < 0) { _score = 0; } else if (value > 100) { _score = 100; } else { _score = value; } } }}Program.cs:
TestScore test = new TestScore();
test.Score = 120;Console.WriteLine($"120を代入:{test.Score}");
test.Score = -10;Console.WriteLine($"-10を代入:{test.Score}");
test.Score = 75;Console.WriteLine($"75を代入:{test.Score}");実行結果:
120を代入:100-10を代入:075を代入:75バッキングフィールドと value
Section titled “バッキングフィールドと value”get/set を明示するときは、内部で値を保持する変数(バッキングフィールド)を別途用意します。
慣習として、先頭にアンダースコアを付けた小文字(_score など)を使うことが多いです。
private int _score; // ← バッキングフィールド(クラス外からは見えない)
public int Score // ← 外向きの窓口{ get { return _score; } set { _score = value; }}set の中で使う value は、プロパティに代入された値を表す特別なキーワードです。
test.Score = 120; // ← この 120 が、set の中で value になる外から代入 → value に入る → バッキングフィールドへ保存 120 120 _score = 100(補正後)set の使い方として、もう一つ典型的なのが 「入力をクリーンに整える」 パターンです。
次の例では、社員名を代入されたときに 前後の空白を取り除いてから バッキングフィールドに保存します。
EmployeeNameHolder.cs:
namespace Ch10_ClassDetails;
class EmployeeNameHolder{ private string _employeeName;
public string EmployeeName { get { return _employeeName; } set { _employeeName = value.Trim(); } }}呼び出し:
EmployeeNameHolder holder = new EmployeeNameHolder();holder.EmployeeName = " 山田二郎 ";
Console.WriteLine($"[{holder.EmployeeName}]");実行結果:
[山田二郎]Trim() は文字列の前後の空白を取り除く string のメソッドです。
利用者は「文字列を渡しただけ」のつもりでも、プロパティが 裏で整形してくれる ようになっています。
このように、set には「補正(TestScore の例)」だけでなく、「整形(本例)」のロジックも書けます。
読み取り専用プロパティ
Section titled “読み取り専用プロパティ”set を持たない、外から代入できない プロパティも作れます。
他のプロパティから計算される値に使うと便利です。
Person.cs:
namespace Ch10_ClassDetails;
class Person{ public string LastName { get; set; } public string FirstName { get; set; }
public string FullName { get { return LastName + FirstName; } }}呼び出し:
Person person = new Person{ LastName = "山田", FirstName = "二郎"};
Console.WriteLine(person.FullName);実行結果:
山田二郎person.FullName = "佐藤昭夫"; のような代入はエラーになります(set がないため)。
読み取り専用プロパティをもう一つ例で見てみましょう。
次の BookProgress は、総ページ数と読了ページ数から 残りページ数 と 完了したかどうか を計算して返します。
BookProgress.cs:
namespace Ch10_ClassDetails;
class BookProgress{ public string Title { get; set; } public int TotalPages { get; set; } public int ReadPages { get; set; }
public int RemainingPages { get { return TotalPages - ReadPages; } }
public bool IsCompleted { get { return ReadPages >= TotalPages; } }}呼び出し:
BookProgress book = new BookProgress{ Title = "C# 入門", TotalPages = 300, ReadPages = 120};
Console.WriteLine($"{book.Title}:残り{book.RemainingPages}ページ、完了:{book.IsCompleted}");実行結果:
C# 入門:残り180ページ、完了:Falseこのクラスは 2 つの読み取り専用プロパティ(RemainingPages と IsCompleted)を持ちます。
TotalPages と ReadPages を変えるたびに、これらの値も連動して変わる仕組みです。
このように、読み取り専用プロパティは「他のプロパティから導き出される値」を表すのに向いています。
アクセス修飾子(public と private)
Section titled “アクセス修飾子(public と private)”ここまで、プロパティやメソッドを書くときに、毎回 public を付けてきました。バッキングフィールドでは private を使ったところもあります。
public string EmployeeName { get; set; } // ← publicprivate int _score; // ← privatepublic や private は アクセス修飾子 と呼ばれ、「そのプロパティ・メソッド・フィールドを、どこから使えるか」を決めます。
| 修飾子 | 意味 | 主な使いどころ |
|---|---|---|
public | どこからでも使える | 外に公開するプロパティ・メソッド |
private | 同じクラスの中だけ で使える | バッキングフィールド、内部処理用のメソッド |
protected | 同じクラス + 派生クラス(継承先)から使える | 第 13 章「継承」の周辺で出てくる(本書では基本使わない) |
internal | 同じプロジェクト内 からだけ使える | ライブラリと業務コードを分ける現場で使う |
本研修では、まず public と private の 2 つを使いこなせるようになれば十分 です。protected と internal は、配属後に現場のコードで出会ったときに調べれば追いつけます。
ここまでの章で public ばかり書いてきたのは、「外から使うプロパティ・メソッドだから」という理由がありました。本章で新しく登場する private は、「外から触らせたくない内部の道具」を表します。
補足:アクセス修飾子を省略するとどうなる?
クラスのメンバー(プロパティ・メソッド・フィールド)で修飾子を省略すると、自動的に
private扱いになります。class自体の修飾子を省略した場合はinternal扱いです。ただし「省略 =
privateのつもり」だと 読み手に伝わりにくい ため、本書ではプロパティ・メソッドには 必ず修飾子を明示 します。
次の private set は、この public と private を プロパティの読み書き(get と set)で別々に指定する 仕組みです。
private set
Section titled “private set”private set を使うと、外からは読み取りだけ・クラスの内部からは変更可能 なプロパティを作れます。
Employee.cs(改造版):
namespace Ch10_ClassDetails;
class Employee{ public int EmployeeId { get; private set; } public string EmployeeName { get; private set; } public string DepartmentName { get; private set; }
public Employee(int employeeId, string employeeName, string departmentName) { EmployeeId = employeeId; EmployeeName = employeeName; DepartmentName = departmentName; }
public void ChangeDepartment(string newDepartmentName) { DepartmentName = newDepartmentName; }}呼び出し:
Employee employee = new Employee(1001, "山田二郎", "総務");
Console.WriteLine(employee.DepartmentName);
employee.ChangeDepartment("開発");Console.WriteLine(employee.DepartmentName);
// employee.EmployeeName = "佐藤昭夫"; ← エラー(private set のため代入不可)実行結果:
総務開発ポイント:
EmployeeIdなどは外から 参照はできる(Console.WriteLine(employee.EmployeeId)は OK)- 外から直接 代入はできない(
employee.EmployeeId = 9999;はエラー) - 値を変更したいときは、用意されたメソッド(
ChangeDepartmentなど)を経由する
このように、変更方法を限定すると、意図しない変更 を防ぎやすくなります。
プロパティの初期化
Section titled “プロパティの初期化”プロパティに 初期値 を設定しておくこともできます。
class TrainingStatus{ public string EmployeeName { get; set; } = "未設定"; public int ProgressRate { get; set; } = 0; public bool IsCompleted { get; set; } = false;}オブジェクトを new した直後から、これらの初期値が入った状態になります。
コンストラクターに書かなくても初期値を持たせられるので、シンプルになります。
ここまでに学んだ次のような工夫は、オブジェクト指向プログラミングで カプセル化 と呼ばれる考え方そのものです。
| 工夫 | 効果 |
|---|---|
private でバッキングフィールドを隠す | 中身に直接触らせない |
set で値の範囲を補正する | 不正な値が入らない |
private set で外からの代入を制限する | 勝手に書き換えられない |
| 変更用のメソッドを用意する | 変更経路がはっきりする |
| コンストラクターで必要な値を必ず受け取る | 中途半端な状態のオブジェクトを作らせない |
カプセル化が目指す 3 つのこと
Section titled “カプセル化が目指す 3 つのこと”カプセル化を一言でいうと、「オブジェクトの内部状態を守り、必要な操作だけを外に公開する」考え方です。 やや細かく見ると、次の 3 つに分けて考えられます。
| 目的 | 何をするか | 本章で使った道具 |
|---|---|---|
| ① 状態を隠す | 内部のフィールドを外から見せない | private フィールド |
| ② 操作を限定する | 値を変える方法を、用意したメソッド経由に絞る | private set + 専用メソッド(例:ChangeDepartment、Deposit / Withdraw) |
| ③ 不正な状態を作らせない | 値の範囲・初期化を強制する | set での補正、引数付きコンストラクター |
なぜカプセル化が大事なのか
Section titled “なぜカプセル化が大事なのか”業務アプリは複数人で開発し、何年も使われます。誰かが「便利だから」と銀行口座の Balance プロパティを account.Balance = 100000; と直接書き換えてしまうと、取引履歴と残高がずれてしまい、原因の追跡に丸一日かかる、ということが起こります。
カプセル化を効かせておくと、「残高が変わった瞬間 = 入出金メソッドが呼ばれた瞬間」 に絞られるため、後から問題を追いかけやすくなります。書く時は少し手間が増えますが、読む時・直す時の手間を大きく減らす のがカプセル化の狙いです。
この先の章でも出てくる
Section titled “この先の章でも出てくる”カプセル化は OOP の基本姿勢なので、本書のこの先でも繰り返し出てきます。
| 章 | カプセル化の出番 |
|---|---|
| 第 13 章(継承) | 親クラスのプロパティを子クラスから安全に変更するために、private set + 専用メソッドを使う |
| 第 14 章(インターフェイス) | 公開する操作だけを interface で約束し、内部実装は隠す |
| 第 23〜25 章(Windows フォーム社員管理) | EmployeeRepository クラスで DB アクセスをまとめ、画面側からは SQL を見せない |
| 第 27〜30 章(Web 社員管理) | 同じく Repository パターンで内部を隠す |
本章では、まず次のように覚えておきましょう。
外から直接変更させたくない値は private set で制限する変更が必要な場合は、メソッドを通して変更させる不正な値が入らないようにプロパティで確認する必要な値はコンストラクターで必ず受け取るよくあるつまずき
Section titled “よくあるつまずき”| つまずき | 原因 | 対応 |
|---|---|---|
return の型が合わない | 戻り値の型と返す値の型が違う | メソッド名の前の型を確認する |
| オーバーロードできない | 引数が同じで戻り値だけ違う | 引数の数や型を変える |
| 省略可能な引数でエラー | 省略可能な引数を前に置いている | 省略可能な引数は後ろに書く |
| コンストラクターに戻り値の型を書いてしまう | 通常メソッドと混同 | コンストラクターには戻り値の型を書かない |
| コンストラクター名を間違える | クラス名と一致していない | コンストラクター名はクラス名と同じ |
new Employee() が使えない | 引数なしコンストラクターが定義されていない | 必要なら引数なしコンストラクターを追加 |
value の意味が分からない | set の仕組みに慣れていない | 「代入された値が value に入る」と覚える |
private set のプロパティに代入できない | 外部からの変更が制限されている | コンストラクターや専用メソッドで変更する |
| 読み取り専用プロパティに代入しようとする | set がない | 計算結果や参照専用の値として使う |
| バッキングフィールドと無限ループ | get { return Score; } のように同名プロパティを呼んでしまう | バッキングフィールド(_score)を返す |
| プロパティ・メソッドを外から呼べない | アクセス修飾子を書き忘れて自動で private 扱いになっている | 外に公開するメンバーには 必ず public を明示 する |
private フィールドを外から使おうとする | クラスの外からは見えない | 外から使うなら public プロパティ経由にする |
学んだことチェック
Section titled “学んだことチェック”-
int・string・bool・自作クラスを戻り値にしたメソッドを書ける - メソッドのオーバーロードを書ける
- 省略可能な引数を持つメソッドを書ける
- 名前付き引数で呼び出せる
- 引数付きコンストラクターを書ける
- コンストラクターをオーバーロードできる
-
this(...)で別のコンストラクターを呼び出せる -
get/setを明示してプロパティを書ける -
valueの役割を説明できる - 読み取り専用プロパティを書ける
-
private setを使える - プロパティに初期値を設定できる
- アクセス修飾子(
public/private)の意味を説明し、使い分けられる - カプセル化が目指す 3 つのこと(状態を隠す / 操作を限定する / 不正な状態を作らせない)を説明できる
研修の進め方によっては、隣の人またはチーム内で説明確認を行います。
次の内容を、自分の言葉で説明してください。
- メソッドのオーバーロードとは何ですか。
- 省略可能な引数はどのようなときに便利ですか。
- コンストラクターはいつ呼び出されますか。
- 引数付きコンストラクターを使うメリットは何ですか。
getとsetは何をするものですか。valueは何を表しますか。private setはどのようなときに使いますか。publicとprivateの違いを、自分の言葉で説明してください。- カプセル化が目指す 3 つのこと(状態を隠す / 操作を限定する / 不正な状態を作らせない)を、覚えている範囲で挙げてください。
説明するときは、完全な答えでなくても構いません。 自分の言葉で説明しようとすることが大切です。
この章の演習課題に取り組みます。
本章では タイマー方式 を試験導入します。次の 3 段階で進めてください。
| 段階 | 時間 | 内容 |
|---|---|---|
| ① 準備 | 10 分(目安) | 上の「ペア確認」と、これから取り組む課題(仕様)の読み込み。ペアや講師に質問してよい |
| ② ソロ作業 | 40 分(タイマーで計測) | 一人で課題に取り組む。タイマーが鳴ったら、完成・未完成にかかわらず作業を止めて提出する |
| ③ チーム時間 | 講師が指定する発表開始時刻まで | チーム内でコードレビューを行い、発表者を決める。実装の続行も可。時間配分はチームで管理する |
第 10 章は コンストラクター・プロパティの高度な使い方 を扱う章で、覚えることが多めです。ソロ作業は 40 分 をとっています。準備フェーズの終わりに講師が号令をかけ、そこからタイマーを開始します。 評価対象はタイマー時点で提出されたコードです(タイマー後に書き足した分は評価には含まれません)。 発表開始時刻は厳守 です。チーム時間中も、その時刻が来たら全員手を止めて発表に移ります。
演習の進め方の詳細は、付録A「演習の進め方」 を参照してください。
この章の演習の進め方
Section titled “この章の演習の進め方”課題はソリューション Kadai10 の中に作成してください。
課題ごとに別のプロジェクトを作成 し、指定されたプロジェクト名を使います。
| 課題 | 必須/発展 | プロジェクト名 | 作成する主なファイル |
|---|---|---|---|
| 課題 10-1 | 必須 | Kd10_01_EmployeeConstructor | Program.cs、Employee.cs |
| 課題 10-2 | 必須 | Kd10_02_SearchOverload | Program.cs、EmployeeSearchService.cs |
| 課題 10-3 | 必須 | Kd10_03_ScoreValidation | Program.cs、TestScore.cs |
| 課題 10-4 | 発展 | Kd10_04_GreetingService | Program.cs、GreetingService.cs |
| 課題 10-5 | 発展 | Kd10_05_BankAccount | Program.cs、BankAccount.cs |
| 課題 10-6 | 発展 | Kd10_06_OrderItem | Program.cs、OrderItem.cs |
フォルダ構成は次のようになります。
Kadai10/ ← 課題用ソリューションフォルダ Kadai10.sln Kd10_01_EmployeeConstructor/ Kd10_02_SearchOverload/ Kd10_03_ScoreValidation/ Kd10_04_GreetingService/ Kd10_05_BankAccount/ Kd10_06_OrderItem/補足:ソリューションに複数のプロジェクトを追加する方法
最初の課題で
Kadai10ソリューションとKd10_01_EmployeeConstructorプロジェクトを同時に作成します。2 つ目以降の課題は、ソリューションエクスプローラーで
Kadai10を右クリックし、追加→新しいプロジェクトから追加します。
演習の共通ルール
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 つ目以降のプロジェクト(Kd10_02_SearchOverload など)も、新規追加するたびに csproj の Nullable を disable に変更する 必要があります。
クラスファイルを自動生成すると、ブロック形式の namespace プロジェクト名 { ... } で作られます(例:課題 10-1 なら namespace Kd10_01_EmployeeConstructor { ... })。本章でもファイルスコープ形式に統一するため、生成後に namespace Kd10_01_EmployeeConstructor; のように ; 形式に書き換えてください。同じプロジェクト内の Program.cs と他のクラスファイルの namespace は揃えます。
提出ルール(タイマー方式)
- 準備フェーズの後、講師の号令で 40 分のタイマー をスタートします
- タイマーが鳴ったら、完成・未完成にかかわらず手を止め、その場で
git add→git commit→git pushを行います- これがこの演習の 唯一の提出 です。タイマー後も実装を続けて構いませんが、書き足した分は評価対象には含まれません
コミットメッセージの形式:
Chapter10 タイマー提出: <どの課題まで完成> / <詰まったポイント>例:
Chapter10 タイマー提出: 10-1〜10-2完成、10-3は途中 / set の中の value の使い方で詰まっているChapter10 タイマー提出: 10-1〜10-3完成、発展未着手 / 特になしChapter10 タイマー提出: 10-1完成、10-2の途中 / コンストラクターの引数の順番でエラーが取れない「どこまでできたか」「詰まったポイント」は短くて構いません。順調なときも「特になし」と明示 してください(順調さの証拠になります)。
提出方法:Git が使えないときはサーバへコピー
Git の状態によっては、講師から「今回は
pushではなくサーバへのフォルダコピーで提出」と指示が出ることがあります。その場合は、pushの代わりにKadai10フォルダをサーバの所定の場所へコピーして提出してください(コピー手順は別途案内済みのとおり)。このとき コミットメッセージが残せない ため、代わりに
提出メモ.txtというテキストファイルを提出先に作成します。コピーが終わってから、次の手順で作成してください。
Kadai10フォルダをコピーした、サーバ上の自分の名前のフォルダをエクスプローラーで開く- 何もないところで右クリック → 新規作成 → テキスト ドキュメント を選ぶ
- ファイル名を
提出メモ.txtに変更する- ダブルクリックで開き、次の内容を書いて保存する
どこまで完成: 10-1〜10-3完成、発展未着手詰まったポイント: 特になし書く内容は、コミットメッセージに書くはずだった「どこまで完成したか」「詰まったポイント」と同じです。順調なときも「詰まったポイント: 特になし」と明示 してください。
タイマー後のチーム時間の使い方
タイマー後、講師が指定する 発表開始時刻 までがチーム時間です。チーム内で次の 3 つを自由に管理してください。
- コードレビュー:他のメンバーの commit を読んで、気づいたことを共有する
- 発表者の選出:発表開始時刻に、チームから 1 人が要点を発表できるよう、誰が話すかを決めておく
- 実装の続行(任意):途中だった課題を続けても構いません(提出後の追加分は評価対象外ですが、理解を深める意義はあります)
どの順番で進めるか、何分ずつ使うかはチームの判断です。ただし 発表開始時刻は厳守 です。その時刻までにレビューと発表者選出を必ず終わらせてください。
まずは、全員が必須課題に取り組んでください。
課題 10-1 引数付きコンストラクター
Section titled “課題 10-1 引数付きコンストラクター”Employee クラスを別ファイル(Employee.cs)に作成し、引数付きコンストラクターで初期値を設定するプログラムを作ってください。
Employee クラスのプロパティ:
| プロパティ名 | 型 | 意味 |
|---|---|---|
EmployeeId | int | 社員番号 |
EmployeeName | string | 氏名 |
DepartmentName | string | 部署 |
Main メソッドの処理:
new Employee(1001, "山田二郎", "総務")でオブジェクトを作成するGetDisplayText()メソッド(社員情報を"1001:山田二郎(総務)"の形式で返す)を呼び出して結果を表示する
実行結果例:
1001:山田二郎(総務)条件:
- 引数付きコンストラクターを定義する
GetDisplayTextメソッドの戻り値はstring- オブジェクト初期化子(
new Employee { ... })は使わない
課題 10-2 メソッドのオーバーロード
Section titled “課題 10-2 メソッドのオーバーロード”EmployeeSearchService クラスを作成し、社員番号と氏名のどちらでも検索できる Search メソッドをオーバーロードで定義してください。
| メソッド | 引数 | 表示内容 |
|---|---|---|
Search(int) | 社員番号 | "社員番号 {番号} で検索します。" |
Search(string) | 氏名 | "氏名 {氏名} で検索します。" |
Main メソッドの処理:
service.Search(1001);とservice.Search("山田二郎");の両方を呼び出す
実行結果例:
社員番号 1001 で検索します。氏名 山田二郎 で検索します。条件:
- 同じ名前
Searchで 2 つのメソッドを定義する - 引数の 型 を変えてオーバーロードする
課題 10-3 プロパティで値を補正する
Section titled “課題 10-3 プロパティで値を補正する”TestScore クラスを作成し、Score プロパティに 0〜100 の範囲外の値が入ったときに自動で補正されるようにしてください。
仕様:
| 代入値 | 保存される値 |
|---|---|
120 | 100 |
-10 | 0 |
75 | 75 |
Main メソッドの処理:
TestScoreオブジェクトを 1 つ作成120・-10・75をそれぞれ代入し、その後Scoreプロパティを表示する
実行結果例:
120を代入した結果:100-10を代入した結果:075を代入した結果:75条件:
- バッキングフィールド
private int _score;を使う getとsetを明示して書くsetの中でvalueを使う
必須課題が終わった人は、発展課題に取り組んでください。 発展課題からは、仕様だけが提示されます。実装方法は自分で考えてください。
課題 10-4 省略可能な引数と名前付き引数
Section titled “課題 10-4 省略可能な引数と名前付き引数”以下の仕様で GreetingService クラスを実装してください。
クラス仕様
- クラス名:
GreetingService - ファイル名:
GreetingService.cs - メソッド:
CreateMessage(string employeeName, string title = "さん", bool exclaim = false):- 戻り値:
string exclaimがfalseのとき:"{employeeName}{title}、こんにちは。"exclaimがtrueのとき:"{employeeName}{title}、こんにちは!!"
- 戻り値:
Main メソッド仕様
次の 3 通りを呼び出して、結果を表示する。
| 呼び出し | 期待する表示 |
|---|---|
CreateMessage("山田二郎") | 山田二郎さん、こんにちは。 |
CreateMessage("田中浩介", "先生") | 田中浩介先生、こんにちは。 |
CreateMessage("佐藤昭夫", exclaim: true) | 佐藤昭夫さん、こんにちは!! |
条件:
- 既定値を持つ引数を使う
- 3 番目の呼び出しでは 名前付き引数 を使う
課題 10-5 private set で守られた BankAccount クラス
Section titled “課題 10-5 private set で守られた BankAccount クラス”第 7 章発展課題 7-4 で作成した BankAccount クラスを、private set を使って 「残高は外から直接書き換えられない、入出金メソッドを通してしか変更できない」 ようにしてください。
第 7 章版との違い:
| 項目 | 第 7 章 7-4(復習) | 本課題(10-5) |
|---|---|---|
| プロパティの公開 | public ... { get; set; }(外から代入可能) | public ... { get; private set; }(外から代入不可) |
| 初期化 | new してからプロパティに代入 | 引数付きコンストラクター で初期化 |
| 残高変更 | 外から直接代入もメソッド経由も可能 | Deposit / Withdraw メソッドを通すしかない |
クラス仕様
- クラス名:
BankAccount - ファイル名:
BankAccount.cs - プロパティ(すべて
public ... { get; private set; }):OwnerName(string):口座名義Balance(int):残高
- コンストラクター:
BankAccount(string ownerName, int initialBalance):名義と初期残高を受け取って初期化する
- メソッド:
Deposit(int amount):amountをBalanceに加算する(戻り値void)Withdraw(int amount):amountをBalanceから減算する(戻り値void)PrintBalance():"名義:{OwnerName} 残高:{Balance}円"を表示する(戻り値void)
Main メソッド仕様
new BankAccount("山田二郎", 0)で口座を作成するPrintBalance()で残高を表示Deposit(50000)で入金 →PrintBalance()で残高を表示Withdraw(20000)で出金 →PrintBalance()で残高を表示
実行結果例:
名義:山田二郎 残高:0円名義:山田二郎 残高:50000円名義:山田二郎 残高:30000円確認事項:
Mainメソッドからaccount.Balance = 100000;のように直接代入しようとするとコンパイルエラーになることを確認する(確認後、その行は削除またはコメントアウトする)- 残高を変更するには
Deposit/Withdrawメソッドを呼ぶしかないことを実感する
補足:この設計の意味
銀行口座の残高を、外部のコードから自由に書き換えられると、取引履歴と残高がずれてしまいます。
private set+ 入出金メソッドという形にすると、残高の変更経路を入出金に限定 できます。これがカプセル化の典型例です。
課題 10-6 OrderItem クラス(統合課題)
Section titled “課題 10-6 OrderItem クラス(統合課題)”注文商品を表す OrderItem クラスを実装してください。
この課題は、この章で学んだ「引数付きコンストラクター」「値の補正」「読み取り専用プロパティ」を組み合わせる統合課題です。
クラス仕様
- クラス名:
OrderItem - プロパティ:
ProductName(string):get; private set;UnitPrice(int):get; private set;(0 未満は 0 に補正)Quantity(int):get; private set;(1 未満は 1 に補正)Amount(int):読み取り専用プロパティ。UnitPrice * Quantityを返す
- コンストラクター:
OrderItem(string productName, int unitPrice, int quantity)- 値の補正はコンストラクターまたは
setのどちらで行ってもよい
Main メソッド仕様
次の OrderItem を作成し、内容を表示する。
OrderItem item = new OrderItem("ノート", 180, 5);実行結果例:
商品名:ノート単価:180円数量:5個金額:900円不正値で作成した場合(例:new OrderItem("ノート", -100, 0))、補正された値が表示されることも確認してください。
提出前チェックリスト
Section titled “提出前チェックリスト”- プログラムを Visual Studio から実行できる
- 戻り値のあるメソッドを作成できている
-
returnする値の型が戻り値の型と合っている - メソッドのオーバーロードを使えている
- 省略可能な引数を使えている(発展)
- 引数付きコンストラクターを書けている
- コンストラクター名がクラス名と一致している
- コンストラクターに戻り値の型を書いていない
-
get/setを明示して書けている -
valueの意味を理解している -
private setを使えている(発展) - 読み取り専用プロパティを使えている(発展)
- クラスを別ファイルに分けて書けている
- インデントが整っている
- 課題を提出した(Git の
commit→push、または講師指示があればKadai10フォルダをサーバへコピーし、提出先フォルダに提出メモ.txtを作成)
Git への提出
Section titled “Git への提出”タイマーが鳴ったら、第 10 章の課題プロジェクト群(Kadai10 ソリューション)をその場で Git に提出します。
git statusgit add .git commit -m "Chapter10 タイマー提出: <どこまで完成> / <詰まったポイント>"git push実行例:
git commit -m "Chapter10 タイマー提出: 10-1〜10-2完成、10-3は途中 / set の中の value の使い方で詰まった"Git の詳しい操作は、付録 C「Git のインストールと提出ルール」 を参照してください。
コミットメッセージの形式は、本章冒頭「演習課題 > 提出ルール(タイマー方式)」を参照してください。
サーバへのコピーで提出する場合
講師から指示があった場合は、
pushの代わりにKadai10フォルダをサーバへコピーして提出します。コピー後、提出先(サーバ上の自分の名前のフォルダ)をエクスプローラーで開き、右クリック → 新規作成 → テキスト ドキュメント で提出メモ.txtを作って「どこまで完成」「詰まったポイント」を書いてください。詳しくは本章冒頭「演習課題 > 提出方法:Git が使えないときはサーバへコピー」を参照してください。
この章のまとめ
Section titled “この章のまとめ”この章では、クラスを使いこなすための機能を学習しました。
- メソッドは
int・string・bool・自作クラスなど、さまざまな型を返せる - 同じ名前で引数の異なるメソッドを複数定義できる(オーバーロード)
- 省略可能な引数を使うと、引数を省略してメソッドを呼び出せる
- 名前付き引数(
引数名: 値)で読みやすく呼び出せる - コンストラクターは、オブジェクト作成時に自動的に呼び出される
- コンストラクターは初期化や必要な値の受け取りに使う
- コンストラクターもオーバーロードできる、
this(...)で別のコンストラクターを呼べる get・setを明示すると、プロパティに処理を加えられるsetの中のvalueは、代入された値を表す- 読み取り専用プロパティ(
setなし)で、外からの代入を禁止できる private setで、外からの直接代入を禁じつつクラス内では変更できるようにできる- プロパティに初期値を直接書ける
- アクセス修飾子は、まず
public(どこからでも)/private(同じクラス内だけ)の 2 つを使い分ければよい(protected/internalは配属後に出会ったときに調べれば追いつける) - これらの工夫は カプセル化 という考え方そのもので、目指すのは「① 状態を隠す」「② 操作を限定する」「③ 不正な状態を作らせない」の 3 つ。第 13 章以降の継承・インターフェイス・Repository パターンでも繰り返し使う
次章では、値型と参照型 を学習します。
これまでに int・bool・string・配列・クラスなど、さまざまな型を使ってきました。
次章では、これらの型が メモリ上でどのように扱われるか に注目し、値型・参照型・構造体・列挙型・null について整理します。
クラスを「中身まで複製したい」「同じ実体を共有したい」といったときに重要になる知識です。