第7章 クラス/オブジェクト指向プログラミングの基礎
この章の目的
Section titled “この章の目的”この章では、C# の重要な考え方である クラス と オブジェクト指向プログラミング(Object-Oriented Programming, OOP)の基礎を学習します。
これまでの章では、変数、条件分岐、繰り返し、配列を使ってプログラムを書いてきました。 これらはどれも、データを扱う手段としては有効ですが、データの種類が増えてくると 「同じものに関する情報がバラバラに散らばる」 という問題が起きます。
クラスを使うと、関連するデータ(社員番号、氏名、部署など)と処理(社員情報を表示する など)を 1 つのまとまり として扱えるようになります。
社員クラスを作る ↓ 設計図として用意社員番号・氏名・部署をひとまとめにする ↓ 情報をまとめる社員情報を表示する処理を持たせる ↓ 処理もまとめる社員オブジェクトを作って利用するこの章では、クラスとオブジェクトの基本的な作り方と使い方を学習します。
この章でできるようになること
Section titled “この章でできるようになること”この章を終えると、次のことができるようになります。
- オブジェクト指向プログラミングの考え方をおおまかに説明できる
- クラスとオブジェクトの違いを説明できる
- 自分で簡単なクラスを作成できる
- クラスにプロパティとメソッドを定義できる
- クラスからオブジェクトを作成して利用できる
- オブジェクト初期化子を使ってプロパティをまとめて設定できる
voidメソッドと戻り値のあるメソッドの違いを説明できる- 引数のあるメソッドを作成できる
- クラスを別ファイルに分けて書ける
- 変数のスコープを説明できる
本章で使用する環境
Section titled “本章で使用する環境”| 項目 | 内容 |
|---|---|
| 開発環境 | Visual Studio 2022 |
| プロジェクト種類 | コンソール アプリ |
| 対象フレームワーク | .NET 8 |
| ソリューション名 | Chapter07 |
| プロジェクト名 | Ch07_Class |
csproj の Nullable は disable に変更してください
プロジェクト作成後、
Ch07_Class.csprojを開き、<Nullable>disable</Nullable>に変更してください。詳しい手順は、第 1 章「1-1 プロジェクトを作成する」を参照してください。
namespace の書き方が変わります
Section titled “namespace の書き方が変わります”これまでの章では、namespace を次のように書いてきました。
namespace Ch06_Array{ internal class Program { static void Main(string[] args) { Console.WriteLine("Hello"); } }}この章からは、次の書き方を使います。
namespace Ch07_Class;
internal class Program{ static void Main(string[] args) { Console.WriteLine("Hello"); }}namespace の後ろに ; を付けて、ファイル全体がその namespace に属する ことを宣言します。
インデントが 1 段浅くなり、コードが読みやすくなります。
これは ファイルスコープ namespace と呼ばれる書き方で、Visual Studio 2022 で新しく作るプロジェクトでは、こちらの書き方が標準になっています。
補足:両方の書き方が現場にあります
現場の C# コードでは、ブロック形式(
namespace XXX { ... })とファイルスコープ形式(namespace XXX;)の両方が混在しています。 古いプロジェクトや、自動生成されたファイルではブロック形式がよく使われます。 どちらも読めるようにしておきましょう。
この章以降のサンプルコードは、すべてファイルスコープ形式で書きます。
作業前チェック
Section titled “作業前チェック”作業を始める前に、次の内容を確認してください。
-
int、stringなどの変数を使える -
if文・for文・foreach文を書ける - 配列の基本(宣言、インデックス、
Length)を説明できる - 第 6 章の内容を Git に提出済みである
7-1 オブジェクト指向とは
Section titled “7-1 オブジェクト指向とは”情報と処理をまとめる考え方
Section titled “情報と処理をまとめる考え方”オブジェクト指向プログラミングとは、プログラムを 「もの」(オブジェクト) の集まりとして考える方法です。
ここでいう「もの」は、現実世界の物体だけではありません。社員、商品、注文、顧客なども、プログラム上の「もの」として扱えます。
それぞれの「もの」には、情報 と 処理 があります。
社員を例にすると、次のように考えられます。
上半分が 情報(社員番号・氏名・部署)、下半分が 処理(社員情報を表示・部署を変更)です。 このように「情報」と「処理」を 1 つのクラスにまとめると、社員に関するものは社員クラスを見ればすべて分かる 状態になります。
C# では、このようなまとまりを クラス として定義します。 そして、そのクラスから実際に作られたものを オブジェクト(または インスタンス)と呼びます。
| 用語 | ひとまずの説明 |
|---|---|
| クラス | 設計図 |
| オブジェクト | 設計図から作られた実物 |
| プロパティ | オブジェクトが持つ情報 |
| メソッド | オブジェクトに行わせる処理 |
設計図と実体の関係を図にすると、次のようになります。
1 つの設計図(クラス)から、何個でも実体(オブジェクト)を作れる のがオブジェクト指向の大きな利点です。
たとえば社員が 100 人いても、Employee クラスは 1 つだけで済みます。
7-2 クラスを使わないとどうなるか
Section titled “7-2 クラスを使わないとどうなるか”1 人分なら問題ない
Section titled “1 人分なら問題ない”クラスを使わずに、社員 1 人分の情報を変数で扱ってみます。
namespace Ch07_Class;
internal class Program{ static void Main(string[] args) { int employeeId = 1001; string employeeName = "山田二郎"; string departmentName = "総務";
Console.WriteLine($"社員番号:{employeeId}"); Console.WriteLine($"氏名:{employeeName}"); Console.WriteLine($"部署:{departmentName}"); }}実行結果:
社員番号:1001氏名:山田二郎部署:総務1 人分ならこれで十分です。
複数人になると分かりにくい
Section titled “複数人になると分かりにくい”社員が 2 人になると、変数が一気に増えます。
int employeeId1 = 1001;string employeeName1 = "山田二郎";string departmentName1 = "総務";
int employeeId2 = 1002;string employeeName2 = "佐藤昭夫";string departmentName2 = "営業";このコードには、次の問題があります。
- 1 人分の情報がひとまとまりになっていない(
1・2の番号で人を区別している) - 変数名が増えて読みにくい
- メールアドレスなど項目を追加すると、全員分の変数を追加する必要がある
配列で多少改善できますが、配列同士の対応関係を人間が注意して管理しなければなりません。
int[] employeeIds = { 1001, 1002 };string[] employeeNames = { "山田二郎", "佐藤昭夫" };string[] departmentNames = { "総務", "営業" };「employeeIds[0] と employeeNames[0] と departmentNames[0] が同じ社員」と人間が覚えておく必要があるのは不便です。
そこで、1 人分の情報をひとまとまりにする ためにクラスを使います。
7-3 初めてのクラス
Section titled “7-3 初めてのクラス”Employee クラスを作る
Section titled “Employee クラスを作る”社員情報をまとめて扱うために、Employee クラスを作成します。
Program.cs に次のコードを入力してください。
namespace Ch07_Class;
internal class Program{ static void Main(string[] args) { Employee employee = new Employee();
employee.EmployeeId = 1001; employee.EmployeeName = "山田二郎"; employee.DepartmentName = "総務";
Console.WriteLine($"社員番号:{employee.EmployeeId}"); Console.WriteLine($"氏名:{employee.EmployeeName}"); Console.WriteLine($"部署:{employee.DepartmentName}"); }}
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } public string DepartmentName { get; set; }}実行結果:
社員番号:1001氏名:山田二郎部署:総務Employee クラスの定義
Section titled “Employee クラスの定義”最後の部分が、社員を表すクラスの定義です。
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } public string DepartmentName { get; set; }}Employee クラスには、次の 3 つの情報を持たせています。
| プロパティ | 型 | 意味 |
|---|---|---|
EmployeeId | int | 社員番号 |
EmployeeName | string | 氏名 |
DepartmentName | string | 部署 |
クラスの中に定義した情報を プロパティ と呼びます。 プロパティは「そのオブジェクトが持っている情報」のことです。
補足:プロパティ名はパスカルケース
C# の慣習では、プロパティ名は 先頭を大文字 にする「パスカルケース」を使います。 変数名(キャメルケース、
employeeId)とは違うルールです。 どちらも単語の区切りが分かりやすくなるための慣習です。
{ get; set; } の意味
Section titled “{ get; set; } の意味”プロパティの定義には { get; set; } という記号が付いています。
public int EmployeeId { get; set; }この章では、次のように覚えてください。
| 記号 | 意味 |
|---|---|
get | プロパティの値を取り出せるようにする |
set | プロパティに値を入れられるようにする |
両方付けると、値の 取り出し と 代入 の両方ができるプロパティになります。
プロパティの内部的な仕組みや、get だけにして読み取り専用にする書き方などは、第 10 章「クラスについて掘り下げる」 で詳しく扱います。
オブジェクトを作成する
Section titled “オブジェクトを作成する”Main メソッドの先頭で、Employee クラスからオブジェクトを作成しています。
Employee employee = new Employee();new Employee() によって、Employee クラスをもとにした実際のオブジェクトが作られます。
Employee クラス(設計図) │ │ new ▼employee オブジェクト(実体)Employee employee の部分は、これまでに見慣れた変数宣言と同じ形です。
ただし型が int や string ではなく、自分で作った クラス名 になっています。
プロパティに値を代入する
Section titled “プロパティに値を代入する”作成したオブジェクトのプロパティには、次のように値を代入できます。
employee.EmployeeId = 1001;employee.EmployeeName = "山田二郎";employee.DepartmentName = "総務";オブジェクト名.プロパティ名 という書き方で、そのオブジェクト固有の情報を扱います。
プロパティへの代入は、通常の変数への代入とほぼ同じ感覚で書けます。
プロパティの値を表示する
Section titled “プロパティの値を表示する”プロパティの値は、変数と同じように参照できます。
Console.WriteLine($"社員番号:{employee.EmployeeId}");Console.WriteLine($"氏名:{employee.EmployeeName}");Console.WriteLine($"部署:{employee.DepartmentName}");文字列補間 $"..." の中でも、{オブジェクト名.プロパティ名} で値を取り出せます。
7-4 複数のオブジェクトを作る
Section titled “7-4 複数のオブジェクトを作る”別々の社員データを持たせる
Section titled “別々の社員データを持たせる”クラスを使う最大のメリットは、同じ設計図から複数の実体を作れる ことです。
次のコードを入力して実行してください。
namespace Ch07_Class;
internal class Program{ static void Main(string[] args) { Employee employee1 = new Employee(); employee1.EmployeeId = 1001; employee1.EmployeeName = "山田二郎"; employee1.DepartmentName = "総務";
Employee employee2 = new Employee(); employee2.EmployeeId = 1002; employee2.EmployeeName = "佐藤昭夫"; employee2.DepartmentName = "営業";
Console.WriteLine($"{employee1.EmployeeId} {employee1.EmployeeName} {employee1.DepartmentName}"); Console.WriteLine($"{employee2.EmployeeId} {employee2.EmployeeName} {employee2.DepartmentName}"); }}
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } public string DepartmentName { get; set; }}実行結果:
1001 山田二郎 総務1002 佐藤昭夫 営業employee1 と employee2 は、同じ Employee クラスから作られていますが、それぞれ別の値を持っています。
オブジェクト初期化子
Section titled “オブジェクト初期化子”プロパティへの代入は、オブジェクトを作るときにまとめて書くこともできます。
Employee employee = new Employee{ EmployeeId = 1001, EmployeeName = "山田二郎", DepartmentName = "総務"};これを オブジェクト初期化子 と呼びます。
new クラス名() の () の代わりに { } を書き、その中に「プロパティ名 = 値」をカンマ区切りで並べます。
通常の代入と比べて、次のように見やすくなります。
| 書き方 | 行数 | 特徴 |
|---|---|---|
| 1 行ずつ代入 | 多い | 順番に書く |
| オブジェクト初期化子 | 1 ブロックにまとまる | 何を作っているかが一目で分かる |
以後のサンプルでは、オブジェクト初期化子を積極的に使います。
クラスとオブジェクトの整理
Section titled “クラスとオブジェクトの整理”これまでに登場した用語を整理します。
| 用語 | 意味 | 例 |
|---|---|---|
| クラス | 設計図 | Employee |
| オブジェクト | 設計図から作られた実体 | employee1 |
| インスタンス | オブジェクトとほぼ同じ意味 | new Employee() で作られたもの |
| プロパティ | オブジェクトが持つ情報 | EmployeeName |
new | 設計図から実体を作る命令 | new Employee() |
「オブジェクト」と「インスタンス」は、この章ではほぼ同じ意味として扱って構いません。
7-5 メソッドを定義する
Section titled “7-5 メソッドを定義する”メソッドとは
Section titled “メソッドとは”メソッドは、処理をまとめたものです。 これまでにも、次のようなメソッドを 呼び出して きました。
Console.WriteLine("こんにちは");int.Parse(input);これらは C# や .NET が用意しているメソッドです。 この章では、自分でクラスの中にメソッドを定義 します。
社員情報を表示するメソッド
Section titled “社員情報を表示するメソッド”社員情報を表示する処理を、Employee クラスの中にまとめます。
namespace Ch07_Class;
internal class Program{ static void Main(string[] args) { Employee employee = new Employee { EmployeeId = 1001, EmployeeName = "山田二郎", DepartmentName = "総務" };
employee.PrintProfile(); }}
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } public string DepartmentName { get; set; }
public void PrintProfile() { Console.WriteLine($"社員番号:{EmployeeId}"); Console.WriteLine($"氏名:{EmployeeName}"); Console.WriteLine($"部署:{DepartmentName}"); }}実行結果:
社員番号:1001氏名:山田二郎部署:総務メソッドを使うことで、Main メソッド側の表示処理が employee.PrintProfile(); の 1 行 に短くなりました。
メソッドの構造
Section titled “メソッドの構造”PrintProfile メソッドの定義を分解します。
public void PrintProfile(){ Console.WriteLine($"社員番号:{EmployeeId}"); Console.WriteLine($"氏名:{EmployeeName}"); Console.WriteLine($"部署:{DepartmentName}");}| 部分 | 意味 |
|---|---|
public | クラスの外から呼び出せる |
void | 戻り値がない(値を返さない) |
PrintProfile | メソッド名 |
() | 引数(後述) |
{ } | 処理の内容 |
メソッドの中では、同じクラスのプロパティ(EmployeeId など)を クラス名を付けずに 直接書けます。
メソッドを呼び出す
Section titled “メソッドを呼び出す”メソッドを使うには、次のように呼び出します。
employee.PrintProfile();これは「employee オブジェクトに対して、自分のプロフィールを表示してください」とお願いするイメージです。
プロパティと違って、メソッドの呼び出しでは 必ず () を付けます。
補足:プロパティとメソッドの見分け方
第 6 章で配列の
Length(プロパティ)とGetLength()(メソッド)の違いに触れました。 一般に、()が付くものがメソッド、付かないものがプロパティです。
種類 例 見方 プロパティ employee.EmployeeName情報を参照している メソッド employee.PrintProfile()処理を呼び出している
戻り値のあるメソッド
Section titled “戻り値のあるメソッド”メソッドは、処理結果を呼び出し元に返す こともできます。 これを 戻り値(返り値)といいます。
次の例では、社員情報を文字列として返す GetProfileText メソッドを作成します。
namespace Ch07_Class;
internal class Program{ static void Main(string[] args) { Employee employee = new Employee { EmployeeId = 1001, EmployeeName = "山田二郎", DepartmentName = "総務" };
string profileText = employee.GetProfileText();
Console.WriteLine(profileText); }}
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } public string DepartmentName { get; set; }
public string GetProfileText() { return $"{EmployeeId}:{EmployeeName}({DepartmentName})"; }}実行結果:
1001:山田二郎(総務)return と戻り値の型
Section titled “return と戻り値の型”戻り値のあるメソッドでは、次の 2 つを書きます。
- メソッド名の前に 戻り値の型 を書く(
string、intなど) - メソッドの中で
return 値;と書く
public string GetProfileText() // ← 戻り値の型は string{ return $"{EmployeeId}:..."; // ← この値が呼び出し元に返る}void と戻り値ありのメソッドを並べると、違いがはっきりします。
| 種類 | 戻り値の型 | return | 使い方 |
|---|---|---|---|
void メソッド | void | 不要(または return;) | employee.PrintProfile(); |
| 戻り値ありメソッド | string、int など | 必要 | string text = employee.GetProfileText(); |
メソッドの中で別のメソッドを呼ぶ
Section titled “メソッドの中で別のメソッドを呼ぶ”メソッドの中からは、同じクラスの別のメソッド も呼び出せます。
Employee クラスに、先ほどの GetProfileText(文字列を返す)と PrintProfile(表示する)の 2 つがあるとします。PrintProfile は、GetProfileText を呼び出して書くこともできます。
public string GetProfileText(){ return $"{EmployeeId}:{EmployeeName}({DepartmentName})";}
public void PrintProfile(){ Console.WriteLine(GetProfileText()); // 同じクラスの GetProfileText を呼ぶ}同じクラスの中なら、プロパティと同じように オブジェクト名. を付けず、メソッド名だけで呼び出せます。
こうしておくと、表示する文字列の形式を変えたいときは GetProfileText の 1 か所を直すだけで済みます。「処理を小さなメソッドに分け、メソッドから別のメソッドを呼ぶ」のは、プログラムを整理する基本的なやり方です。
引数のあるメソッド
Section titled “引数のあるメソッド”メソッドには、外部から値を渡す こともできます。 これを 引数 といいます。
次の例では、部署名を変更する ChangeDepartment メソッドを作成します。
namespace Ch07_Class;
internal class Program{ static void Main(string[] args) { Employee employee = new Employee { EmployeeId = 1001, EmployeeName = "山田二郎", DepartmentName = "総務" };
Console.WriteLine($"変更前:{employee.DepartmentName}");
employee.ChangeDepartment("開発");
Console.WriteLine($"変更後:{employee.DepartmentName}"); }}
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } public string DepartmentName { get; set; }
public void ChangeDepartment(string newDepartment) { DepartmentName = newDepartment; }}実行結果:
変更前:総務変更後:開発引数の仕組み
Section titled “引数の仕組み”引数は、メソッド名の () の中に「型 変数名」で書きます。
public void ChangeDepartment(string newDepartment){ DepartmentName = newDepartment;}呼び出すときに渡した値が、引数の変数に入ります。
employee.ChangeDepartment("開発"); ↓ string newDepartment = "開発"; (メソッドの中) ↓ DepartmentName = newDepartment;引数は複数並べることもできます(カンマ区切り)。
public void ChangeInfo(string newName, string newDepartment){ EmployeeName = newName; DepartmentName = newDepartment;}呼び出すときも、同じ順番で値を渡します。
employee.ChangeInfo("佐藤昭夫", "営業");売上金額を計算する例
Section titled “売上金額を計算する例”Employee 以外の例として、売上情報を表す Sale クラスを作ります。
namespace Ch07_Class;
internal class Program{ static void Main(string[] args) { Sale sale = new Sale { ProductName = "ノート", UnitPrice = 180, Quantity = 5 };
int amount = sale.GetAmount();
Console.WriteLine($"商品名:{sale.ProductName}"); Console.WriteLine($"金額:{amount}円"); }}
class Sale{ public string ProductName { get; set; } public int UnitPrice { get; set; } public int Quantity { get; set; }
public int GetAmount() { return UnitPrice * Quantity; }}実行結果:
商品名:ノート金額:900円GetAmount メソッドは、UnitPrice * Quantity を計算し、int 型の値として返しています。
7-6 メソッドの利点
Section titled “7-6 メソッドの利点”メソッドを使うことで、次のメリットが得られます。
| メリット | 内容 |
|---|---|
| 同じ処理を書かなくてよい | 表示処理を 1 か所にまとめられる |
| 処理の意味が分かりやすい | employee.PrintProfile() の 1 行で目的が伝わる |
| 修正がしやすい | 表示形式を変えたいとき、メソッドの中だけ修正すれば全員に反映される |
| 複数オブジェクトで使える | employee1.PrintProfile() も employee2.PrintProfile() も同じメソッドで動く |
例として、社員情報の表示形式を変えたい場合を考えます。
public void PrintProfile(){ Console.WriteLine($"[{EmployeeId}] {EmployeeName} / {DepartmentName}");}このように PrintProfile の中身を変えるだけで、Main メソッドに何度も書いた employee.PrintProfile(); の表示形式がすべて変わります。
表示処理を直接書いていたら、出てくる場所すべてを書き直す必要があります。
7-7 クラスを別ファイルに分ける
Section titled “7-7 クラスを別ファイルに分ける”現場ではクラスごとにファイルを分ける
Section titled “現場ではクラスごとにファイルを分ける”これまでのサンプルでは、Program.cs の中に Employee クラスも一緒に書いていました。
Program.cs├─ class Program└─ class Employee学習用には便利ですが、現場のプロジェクトでは 1 つのクラスを 1 つのファイルに書く のが一般的です。 クラスが増えても、ファイル名でどこに書いてあるかすぐ分かります。
Chapter07/ ← ソリューションフォルダ Chapter07.sln Ch07_Class/ ← プロジェクトフォルダ Program.cs ← class Program Employee.cs ← class EmployeeEmployee.cs を追加する
Section titled “Employee.cs を追加する”Visual Studio で新しいクラスファイルを追加する手順は、次のとおりです。
- ソリューションエクスプローラーで、プロジェクト名
Ch07_Classを右クリック - 追加 → クラス を選択
- 名前に
Employee.csと入力して 追加 をクリック
Employee.cs が作成されると、次のようなファイルが自動で出来上がります。
namespace Ch07_Class{ internal class Employee { }}Visual Studio が自動生成するファイルでは、ブロック形式の namespace になっています。 この章ではファイルスコープ形式に統一するため、次のように書き直します。
namespace Ch07_Class;
class Employee{ public int EmployeeId { get; set; } public string EmployeeName { get; set; } public string DepartmentName { get; set; }
public void PrintProfile() { Console.WriteLine($"社員番号:{EmployeeId}"); Console.WriteLine($"氏名:{EmployeeName}"); Console.WriteLine($"部署:{DepartmentName}"); }}Program.cs から呼び出す
Section titled “Program.cs から呼び出す”Program.cs 側からは、これまでと同じように Employee クラスを使えます。
namespace Ch07_Class;
internal class Program{ static void Main(string[] args) { Employee employee = new Employee { EmployeeId = 1001, EmployeeName = "山田二郎", DepartmentName = "総務" };
employee.PrintProfile(); }}Program.cs と Employee.cs の namespace を揃えておけば、using を書かなくても互いに参照できます。
今後この研修では、自作クラスはこの方法で別ファイルに分けて書くことを基本とします。
7-8 変数のスコープ
Section titled “7-8 変数のスコープ”スコープとは
Section titled “スコープとは”スコープ とは、変数を使える範囲のことです。
C# では、変数は宣言した { } の中で使えます。
namespace Ch07_Class;
internal class Program{ static void Main(string[] args) { int number = 10; // ← Main メソッド内で宣言 Console.WriteLine(number); // ← OK(同じ {} の中) }}number は Main メソッドの { } の中で宣言されているので、その中だけで使えます。
スコープの代表的なケース
Section titled “スコープの代表的なケース”| 宣言場所 | 使える範囲 |
|---|---|
| メソッドの中 | そのメソッドの中だけ |
if 文の { } の中 | その if 文の中だけ |
for 文の ( ) の中(カウンタ変数) | その for 文の中だけ |
| クラスの中(プロパティ) | そのクラスのすべてのメソッド |
よくあるスコープのミス
Section titled “よくあるスコープのミス”if 文の中で宣言した変数を外で使えない
Section titled “if 文の中で宣言した変数を外で使えない”次のコードはエラーになります。
int score = 80;
if (score >= 60){ string result = "合格"; // ← if の中で宣言}
Console.WriteLine(result); // ← エラー:result は使えないresult は if 文の { } の中で宣言されているため、外では使えません。
外でも使いたい場合は、外側で宣言します。
int score = 80;string result; // ← 外で宣言
if (score >= 60){ result = "合格";}else{ result = "不合格";}
Console.WriteLine(result); // ← OK同じ名前でも別の変数になる
Section titled “同じ名前でも別の変数になる”メソッドごとに変数のスコープは独立します。
staticについて(この章では深追いしません)下のサンプルでは、
Programクラスの中にstatic void PrintNumber()というメソッドを追加しています。 7-5 で学んだpublic void PrintProfile()(Employeeクラスのメソッド)とは違い、staticという修飾子が付いています。ここでは「
Mainメソッドと同じクラス内に書く補助メソッドにはstaticを付ける」とだけ覚えておけば十分です。staticの意味そのものは 次章「第 8 章 静的メソッド/静的プロパティ/静的クラス」 で詳しく学習します。
namespace Ch07_Class;
internal class Program{ static void Main(string[] args) { int number = 10; Console.WriteLine(number);
PrintNumber();
Console.WriteLine(number); }
static void PrintNumber() { int number = 123; // ← Main の number とは別物 Console.WriteLine(number); }}実行結果:
1012310Main メソッドの number と PrintNumber メソッドの number は、同じ名前でも別の変数です。
スコープを意識する理由
Section titled “スコープを意識する理由”スコープを理解すると、次のミスを減らせます。
- 「
nameが見つかりません」というエラー - 同じ名前の変数を、つい同じものだと思ってしまうミス
- 必要以上に広い範囲で変数を使ってしまうミス
変数は 必要な範囲だけで使えるようにする のが基本です。
コラム:コメントの書き方
Section titled “コラム:コメントの書き方”第 7 章まで来て、クラス・メソッド・複数ファイルと、コードの構造が一気に増えてきました。コードが複雑になるほど、コメント で意図を書き残しておく価値が高まります。第 1 章で // の書き方は学びましたが、ここで「どう書くとよいか」を簡単に整理します。
コメントは誰のためのものか。 コメントは、後でコードを読む人 ── 数か月後の自分や、一緒に開発するチームの人 ── への手紙です。書いた直後は当たり前に思えても、時間が経つと自分でも忘れます。
「なぜ」を書く ── 意図コメント。 コードを見れば分かる「何をしているか」よりも、「なぜそうするのか」「ぱっと見では分からない意図」を書いたコメントを、ここでは 意図コメント と呼びます。意図コメントは、実務でも研修でも役立つ良い習慣です。
// 退職者は一覧に出さないため、在籍者だけを数える逆に、コードを読めばすぐ分かることを、そのまま日本語にしただけのコメントは、あまり意味がありません。
total = total + price; // total に price を足すこのコメントは、コードを言い換えているだけで、読む人に新しい情報を与えていません。
気をつけたいことが 2 つあります。
| 注意点 | 理由 |
|---|---|
| 書きすぎない | すべての行にコメントを付けると、かえって読みにくくなる |
| コメントを古いままにしない | コードを直したらコメントも直す。中身と食い違ったコメントは、無いより危険(読む人をだましてしまう) |
プログラムの先頭に「ヘッダーコメント」を付ける。 ファイルの先頭に、そのプログラムの概要を数行で書いておくと、開いてすぐ何のコードか分かります。この研修の演習課題では、Program.cs の先頭に「課題番号」「プログラムの目的」「自分の氏名」を書きます。
// 課題7-1 Employee クラスの作成// 社員1人分の情報をクラスにまとめて表示するプログラム// 氏名:山田二郎研修のルールと実務の違い
ヘッダーコメントに氏名や課題番号を書くのは この研修のルール です。講師が「誰の・どの課題か」を確認するためで、フォルダのコピー提出とも合わせています。 実務では、ファイル先頭コメントの書き方はプロジェクトごとに異なります。氏名は変更履歴(Git など)で分かるため書かないことも多く、配属先のルールに従うのが基本です。
クラスやメソッドには「役割」を一言。 この章で学んだクラスやメソッドは、上に「何のためのものか」を 1 行添えておくと、他の人がすぐ理解できます。
// 社員 1 人分の情報をまとめて扱うクラスclass Employee{ ...}この章以降の演習課題では、ヘッダーコメント と、主要な処理への 意図コメント を必ず入れます。
実務では、コメントは必要なところに絞るのが基本です。ただしこの研修では、コメントも評価の対象としています。特に必須課題は易しいぶん、コメントを書く練習の場でもあります。学習の段階なので、自分の考えや意図を、普段より少し丁寧にコメントで残して構いません。大切なのは行数ではなく、「なぜそうしたか」が読み手に伝わることです。
よくあるつまずき
Section titled “よくあるつまずき”| つまずき | 原因 | 対応 |
|---|---|---|
| クラスとオブジェクトの違いが分からない | 設計図と実体の区別が曖昧 | クラスは設計図、オブジェクトはそこから作られた実体 |
new Employee() の意味が分からない | クラスからオブジェクトを作る処理に慣れていない | new は「設計図から実物を作る命令」と考える |
| プロパティと変数の違いが曖昧 | オブジェクトに属するかどうかを意識していない | employee.Name のように オブジェクト名. が付くのがプロパティ |
メソッド呼び出しで () を忘れる | プロパティと混同している | メソッドには必ず () が付く |
return を書き忘れる | 戻り値のあるメソッドの理解が不十分 | 戻り値の型が void 以外なら return が必要 |
void の意味が分からない | 戻り値の有無を意識していない | void は「値を返さない」 |
| クラスの中に書く場所が分からない | Program クラスと自作クラスの位置関係に慣れていない | 別ファイル(Employee.cs)を作るのが基本 |
| 変数が使えないエラーが出る | スコープの範囲外で使っている | 変数を宣言した { } を確認する |
if の中で宣言した変数が外で使えない | ブロック内だけ有効な変数になっている | 外でも使うなら外側で宣言する |
namespace のセミコロンを忘れる | ファイルスコープ形式に慣れていない | namespace XXX; のように末尾に ; を付ける |
学んだことチェック
Section titled “学んだことチェック”次の項目について、自分で説明できるか確認してください。
- オブジェクト指向の考え方をおおまかに説明できる
- クラスとオブジェクトの違いを説明できる
- クラスにプロパティを定義できる
-
newを使ってオブジェクトを作成できる - オブジェクト初期化子を使える
- クラスにメソッドを定義できる
-
voidと戻り値ありメソッドの違いを説明できる - 引数のあるメソッドを作成できる
- クラスを別ファイルに分けて書ける
- ファイルスコープ namespace の書き方を説明できる
- スコープとは何かを説明できる
研修の進め方によっては、隣の人またはチーム内で説明確認を行います。
次の内容を、自分の言葉で説明してください。
- クラスとオブジェクトの違いは何ですか。
new Employee()は何をしているコードですか。- プロパティとメソッドの違いは何ですか。
voidはどのような意味ですか。returnは何のために使いますか。- 引数とは何ですか。
- クラスを別ファイルに分けると、どんなメリットがありますか。
- スコープとは何ですか。
説明するときは、完全な答えでなくても構いません。 自分の言葉で説明しようとすることが大切です。
講義中演習課題
Section titled “講義中演習課題”ここまでに学んだ クラス・プロパティ・メソッド を、講義の中で実際に手を動かして確認します。オブジェクト指向は、読むだけでは身につきにくいためです。
次の 3 問は、講師と一緒に進める 講義中の演習 です。講義用ソリューション Chapter07(プロジェクト Ch07_Class)の中で作成します。提出はしません(提出する課題は、後の「演習課題」です)。
課題 7-1 Employee クラスを作成する
Section titled “課題 7-1 Employee クラスを作成する”社員情報を表す Employee クラスを別ファイル(Employee.cs)に作成し、Main メソッドで 1 人分のデータを表示してください。
Employee クラスには、次のプロパティを持たせます。
| プロパティ名 | 型 | 意味 |
|---|---|---|
EmployeeId | int | 社員番号 |
EmployeeName | string | 氏名 |
DepartmentName | string | 部署 |
実行結果例:
社員番号:1001氏名:山田二郎部署:総務条件:
EmployeeクラスはEmployee.csに書く- オブジェクト初期化子で値を設定する
Program.csのMainメソッドで表示する
課題 7-2 PrintProfile メソッドを追加する
Section titled “課題 7-2 PrintProfile メソッドを追加する”課題 7-1 の Employee クラスに、PrintProfile メソッドを追加してください。
表示処理を Employee クラスの中に移し、Main メソッドからは employee.PrintProfile(); の 1 行で表示できるようにします。
実行結果例:
社員番号:1001氏名:山田二郎部署:総務条件:
EmployeeクラスにPrintProfileメソッドを定義する- 戻り値の型は
void Mainメソッドからemployee.PrintProfile();で呼び出す
課題 7-3 複数の Employee オブジェクトを作成する
Section titled “課題 7-3 複数の Employee オブジェクトを作成する”Employee オブジェクトを 2 つ作成し、それぞれの社員情報を表示してください。
2 人とも PrintProfile メソッドを使って表示します。
実行結果例:
社員番号:1001氏名:山田二郎部署:総務社員番号:1002氏名:佐藤昭夫部署:営業条件:
Employeeオブジェクトを 2 つ作成する- それぞれ異なる値を設定する(
employeesテーブルから選んで構いません) - 両方とも
PrintProfileメソッドで表示する
この章の演習課題に取り組みます。
本章では タイマー方式 を試験導入します。次の 3 段階で進めてください。
| 段階 | 時間 | 内容 |
|---|---|---|
| ① 準備 | 10 分(目安) | 上の「ペア確認」と、これから取り組む課題(仕様)の読み込み。ペアや講師に質問してよい |
| ② ソロ作業 | 35 分(タイマーで計測) | 一人で課題に取り組む。タイマーが鳴ったら、完成・未完成にかかわらず作業を止めて提出する |
| ③ チーム時間 | 講師が指定する発表開始時刻まで | チーム内でコードレビューを行い、発表者を決める。実装の続行も可。時間配分はチームで管理する |
準備フェーズの終わりに講師が号令をかけ、そこから ソロ作業の 35 分タイマー を開始します。 評価対象はタイマー時点で提出されたコードです(タイマー後に書き足した分は評価には含まれません)。 発表開始時刻は厳守 です。チーム時間中も、その時刻が来たら全員手を止めて発表に移ります。
演習の進め方の詳細は、付録A「演習の進め方」 を参照してください。
この章の演習の進め方
Section titled “この章の演習の進め方”課題はソリューション Kadai07 の中に作成してください。
課題ごとに別のプロジェクトを作成 し、指定されたプロジェクト名を使います。
| 課題 | プロジェクト名 | 作成する主なファイル |
|---|---|---|
| 課題 7-1a | Kd07_01a_ProductClass | Program.cs、Product.cs |
| 課題 7-2a | Kd07_02a_PrintInfo | Program.cs、Product.cs |
| 課題 7-3a | Kd07_03a_MultipleProducts | Program.cs、Product.cs |
| 課題 7-4 | Kd07_04_BankAccount | Program.cs、BankAccount.cs |
| 課題 7-5 | Kd07_05_SalaryGrade | Program.cs、Employee.cs |
| 課題 7-6 | Kd07_06_TopSalary | Program.cs、Employee.cs |
フォルダ構成は次のようになります。
Kadai07/ ← 課題用ソリューションフォルダ Kadai07.sln Kd07_01a_ProductClass/ Kd07_02a_PrintInfo/ Kd07_03a_MultipleProducts/ Kd07_04_BankAccount/ Kd07_05_SalaryGrade/ Kd07_06_TopSalary/課題 7-1a〜7-3a は必須、課題 7-4〜7-6 は発展です。
補足:ソリューションに複数のプロジェクトを追加する方法
最初の課題で
Kadai07ソリューションとKd07_01a_ProductClassプロジェクトを同時に作成します。2 つ目以降の課題は、ソリューションエクスプローラーで
Kadai07を右クリックし、追加→新しいプロジェクトから追加します。
演習の共通ルール
Section titled “演習の共通ルール”以下は、本章のすべての課題に共通する作業です。各課題の本文には繰り返し書きません。
| 作業 | 内容 | 参照 |
|---|---|---|
| プロジェクト作成時の設定 | 「最上位レベルのステートメントを使用しない」にチェック | 第 1 章 1-1 |
| csproj の編集 | <Nullable>disable</Nullable> に変更 | 第 1 章 1-1 |
namespace の書き方 | ファイルスコープ形式(namespace XXX;)で書く | 本章 冒頭 |
| クラスファイルの追加 | ソリューションエクスプローラーでプロジェクトを右クリック → 追加 → クラス | 本章 7-7 |
| ファイルを保存して実行 | Ctrl + S で保存 → F5 で実行 | 第 1 章 1-2 |
特に 2 つ目以降のプロジェクト(Kd07_02a_PrintInfo など)も、新規追加するたびに csproj の Nullable を disable に変更する 必要があります。
クラスファイルを自動生成すると、ブロック形式の namespace プロジェクト名 { ... } で作られます(例:課題 7-1a なら namespace Kd07_01a_ProductClass { ... })。本章ではファイルスコープ形式に統一するため、生成後に namespace Kd07_01a_ProductClass; のように ; 形式に書き換えてください。Program.cs とクラスファイルの namespace は揃えます。
提出ルール(タイマー方式)
- 準備フェーズの後、講師の号令で 35 分のタイマー をスタートします
- タイマーが鳴ったら、完成・未完成にかかわらず手を止め、その場で
git add→git commit→git pushを行います- これがこの演習の 唯一の提出 です。タイマー後も実装を続けて構いませんが、書き足した分は評価対象には含まれません
コミットメッセージの形式:
Chapter07 タイマー提出: <どの課題まで完成> / <詰まったポイント>例:
Chapter07 タイマー提出: 7-1a〜7-2a完成、7-3aは途中 / 7-3a の 2 つ目のオブジェクトの作り方で詰まっているChapter07 タイマー提出: 7-1a〜7-3a全て完成、発展未着手 / 特になしChapter07 タイマー提出: 7-1aの途中まで / 別ファイルのクラスが Program.cs から見つけられない「どこまでできたか」「詰まったポイント」は短くて構いません。順調なときも「特になし」と明示 してください(順調さの証拠になります)。
提出方法:Git が使えないときはサーバへコピー
Git の状態によっては、講師から「今回は
pushではなくサーバへのフォルダコピーで提出」と指示が出ることがあります。その場合は、pushの代わりにKadai07フォルダをサーバの所定の場所へコピーして提出してください(コピー手順は別途案内済みのとおり)。このとき コミットメッセージが残せない ため、代わりに
提出メモ.txtというテキストファイルを提出先に作成します。コピーが終わってから、次の手順で作成してください。
Kadai07フォルダをコピーした、サーバ上の自分の名前のフォルダをエクスプローラーで開く- 何もないところで右クリック → 新規作成 → テキスト ドキュメント を選ぶ
- ファイル名を
提出メモ.txtに変更する- ダブルクリックで開き、次の内容を書いて保存する
どこまで完成: 7-1a〜7-3a完成、発展未着手詰まったポイント: 特になし書く内容は、コミットメッセージに書くはずだった「どこまで完成したか」「詰まったポイント」と同じです。順調なときも「詰まったポイント: 特になし」と明示 してください。
タイマー後のチーム時間の使い方
タイマー後、講師が指定する 発表開始時刻 までがチーム時間です。チーム内で次の 3 つを自由に管理してください。
- コードレビュー:他のメンバーの commit を読んで、気づいたことを共有する
- 発表者の選出:発表開始時刻に、チームから 1 人が要点を発表できるよう、誰が話すかを決めておく
- 実装の続行(任意):途中だった課題を続けても構いません(提出後の追加分は評価対象外ですが、理解を深める意義はあります)
どの順番で進めるか、何分ずつ使うかはチームの判断です。ただし 発表開始時刻は厳守 です。その時刻までにレビューと発表者選出を必ず終わらせてください。
まずは、全員が必須課題に取り組んでください。講義中演習課題と似た構成ですが、別の題材(商品)で取り組みます。
課題 7-1a Product クラスを作成する
Section titled “課題 7-1a Product クラスを作成する”商品情報を表す Product クラスを別ファイル(Product.cs)に作成し、Main メソッドで 1 つ分のデータを表示してください。
Product クラスには、次のプロパティを持たせます。
| プロパティ名 | 型 | 意味 |
|---|---|---|
ProductId | int | 商品番号 |
ProductName | string | 商品名 |
Price | int | 価格 |
実行結果例:
商品番号:101商品名:ノート価格:180円条件:
ProductクラスはProduct.csに書く- オブジェクト初期化子で値を設定する
Program.csのMainメソッドで表示する
課題 7-2a PrintInfo メソッドを追加する
Section titled “課題 7-2a PrintInfo メソッドを追加する”課題 7-1a の Product クラスに、PrintInfo メソッドを追加してください。
表示処理を Product クラスの中に移し、Main メソッドからは product.PrintInfo(); の 1 行で表示できるようにします。
実行結果例:
商品番号:101商品名:ノート価格:180円条件:
ProductクラスにPrintInfoメソッドを定義する- 戻り値の型は
void Mainメソッドからproduct.PrintInfo();で呼び出す
課題 7-3a 複数の Product オブジェクトを作成する
Section titled “課題 7-3a 複数の Product オブジェクトを作成する”Product オブジェクトを 2 つ作成し、それぞれの商品情報を表示してください。
2 つとも PrintInfo メソッドを使って表示します。
実行結果例:
商品番号:101商品名:ノート価格:180円商品番号:102商品名:ボールペン価格:120円条件:
Productオブジェクトを 2 つ作成する- それぞれ異なる値を設定する
- 両方とも
PrintInfoメソッドで表示する
必須課題が終わった人は、発展課題に取り組んでください。 発展課題からは、仕様だけが提示されます。実装方法は自分で考えてください。
課題 7-4 BankAccount クラスを作成する
Section titled “課題 7-4 BankAccount クラスを作成する”以下の仕様で、銀行口座を表す BankAccount クラスを実装してください。
クラス仕様
- クラス名:
BankAccount - ファイル名:
BankAccount.cs - プロパティ:
OwnerName(string):口座名義Balance(int):残高
- メソッド:
Deposit(int amount):Balanceにamountを加算する(戻り値void)Withdraw(int amount):Balanceからamountを減算する(戻り値void)PrintBalance():口座名義と残高を表示する(戻り値void)
Main メソッド仕様
BankAccountオブジェクトを 1 つ作成する(名義「山田二郎」、残高 0)- 残高を表示する → 50000 円を入金する → 残高を表示する → 20000 円を出金する → 残高を表示する
実行結果例:
名義:山田二郎 残高:0円名義:山田二郎 残高:50000円名義:山田二郎 残高:30000円課題 7-5 給与等級を判定するメソッドを作成する
Section titled “課題 7-5 給与等級を判定するメソッドを作成する”以下の仕様で Employee クラスを実装してください。
クラス仕様
- クラス名:
Employee - ファイル名:
Employee.cs - プロパティ:
EmployeeName(string):氏名Salary(int):給与
- メソッド:
GetSalaryGrade():string型の給与等級を返す(判定基準は下表)
GetSalaryGrade() の判定基準:
| 給与 | 返す文字列 |
|---|---|
| 600000 以上 | 上級 |
| 400000 以上 600000 未満 | 中級 |
| 400000 未満 | 初級 |
Main メソッド仕様
Employeeオブジェクトを 2 人作成する(給与の異なる社員)- それぞれの氏名と給与等級を表示する
実行結果例:
山田二郎:中級佐々木明子:上級ヒント
GetSalaryGrade()の中でif/else ifを使ってSalaryを判定し、returnで文字列を返します。
課題 7-6 配列の中から最高給与の社員を探す
Section titled “課題 7-6 配列の中から最高給与の社員を探す”以下の仕様で、複数社員の中から給与が最も高い社員を探すプログラムを作成してください。
クラス仕様
- クラス名:
Employee - ファイル名:
Employee.cs - プロパティ:
EmployeeName(string):氏名Salary(int):給与
Main メソッド仕様
Employee[]型の配列に、次の 4 人分の社員データを格納する- 全員の氏名と給与を一覧表示する
- 配列の中で 給与が最も高い社員 を探し、その氏名を表示する
使用する社員データ
| 氏名 | 給与 |
|---|---|
| 山田二郎 | 500000 |
| 佐藤昭夫 | 500000 |
| 佐々木明子 | 800000 |
| 中山大輔 | 400000 |
実行結果例:
山田二郎:500000円佐藤昭夫:500000円佐々木明子:800000円中山大輔:400000円最高給与:佐々木明子さんヒント
第 6 章の「最高得点者を求める」と同じ考え方です。最高給与と、そのときの社員を覚えておく変数を用意し、
foreachまたはforで配列をまわして、より高い給与が見つかったら更新します。
提出前チェックリスト
Section titled “提出前チェックリスト”- プログラムを Visual Studio から実行できる
- クラスを別ファイル(
Employee.csなど)に書けている -
namespace XXX;のファイルスコープ形式で書けている - クラスにプロパティを定義できている
-
newを使ってオブジェクトを作成できている - オブジェクト初期化子を使えている
- クラスにメソッドを定義できている
- 戻り値のあるメソッドで
returnを使えている - 引数のあるメソッドを作成できている
- インデントが整っている
- 課題を提出した(Git の
commit→push、または講師指示があればKadai07フォルダをサーバへコピーし、提出先フォルダに提出メモ.txtを作成)
Git への提出
Section titled “Git への提出”タイマーが鳴ったら、第 7 章の課題プロジェクト群(Kadai07 ソリューション)をその場で Git に提出します。
git statusgit add .git commit -m "Chapter07 タイマー提出: <どこまで完成> / <詰まったポイント>"git push実行例:
git commit -m "Chapter07 タイマー提出: 7-1a〜7-2a完成、7-3aは途中 / 7-3a の 2 つ目のオブジェクトの作り方で悩んだ"Git の詳しい操作は、付録 C「Git のインストールと提出ルール」 を参照してください。
コミットメッセージの形式は、本章冒頭「演習課題 > 提出ルール(タイマー方式)」を参照してください。
サーバへのコピーで提出する場合
講師から指示があった場合は、
pushの代わりにKadai07フォルダをサーバへコピーして提出します。コピー後、提出先(サーバ上の自分の名前のフォルダ)をエクスプローラーで開き、右クリック → 新規作成 → テキスト ドキュメント で提出メモ.txtを作って「どこまで完成」「詰まったポイント」を書いてください。詳しくは本章冒頭「演習課題 > 提出方法:Git が使えないときはサーバへコピー」を参照してください。
この章のまとめ
Section titled “この章のまとめ”この章では、クラスとオブジェクト指向プログラミングの基礎を学習しました。
主な内容は次のとおりです。
- オブジェクト指向では、関連する情報と処理を 1 つのまとまりとして考える
- クラスはオブジェクトを作るための設計図、オブジェクトはそこから作られた実体
- プロパティはオブジェクトが持つ情報、メソッドはオブジェクトが行う処理
newを使うとクラスからオブジェクトを作成できる- オブジェクト初期化子を使うと、プロパティの値をまとめて設定できる
voidは値を返さないメソッド、戻り値のあるメソッドではreturnを使う- メソッドには引数を渡して外部から値を受け取れる
- クラスは別ファイルに分けて書くのが基本(
Employee.csのように) - この章から、
namespace XXX;のファイルスコープ形式を使う - 変数には使える範囲(スコープ)があり、宣言した
{ }の中で使える
次章では、静的メソッド・静的プロパティ・静的クラス(static)を学習します。
この章では、new Employee() でオブジェクトを作ってからプロパティやメソッドを使いました。
次章では、Console.WriteLine や int.Parse のように、オブジェクトを作らずに呼び出せる機能 について学習します。
// この章で学んだ書き方Employee employee = new Employee();employee.PrintProfile();
// 次章で学ぶ書き方Console.WriteLine("Hello"); // ← Console オブジェクトを new していないint n = int.Parse("123"); // ← int オブジェクトを new していない「クラス名.メソッド名()」という見覚えのある書き方の仕組みが、次の章で分かります。