Skip to content

第7章 クラス/オブジェクト指向プログラミングの基礎

この章では、C# の重要な考え方である クラスオブジェクト指向プログラミング(Object-Oriented Programming, OOP)の基礎を学習します。

これまでの章では、変数、条件分岐、繰り返し、配列を使ってプログラムを書いてきました。 これらはどれも、データを扱う手段としては有効ですが、データの種類が増えてくると 「同じものに関する情報がバラバラに散らばる」 という問題が起きます。

クラスを使うと、関連するデータ(社員番号、氏名、部署など)と処理(社員情報を表示する など)を 1 つのまとまり として扱えるようになります。

社員クラスを作る
↓ 設計図として用意
社員番号・氏名・部署をひとまとめにする
↓ 情報をまとめる
社員情報を表示する処理を持たせる
↓ 処理もまとめる
社員オブジェクトを作って利用する

この章では、クラスとオブジェクトの基本的な作り方と使い方を学習します。


この章でできるようになること

Section titled “この章でできるようになること”

この章を終えると、次のことができるようになります。

  • オブジェクト指向プログラミングの考え方をおおまかに説明できる
  • クラスとオブジェクトの違いを説明できる
  • 自分で簡単なクラスを作成できる
  • クラスにプロパティとメソッドを定義できる
  • クラスからオブジェクトを作成して利用できる
  • オブジェクト初期化子を使ってプロパティをまとめて設定できる
  • void メソッドと戻り値のあるメソッドの違いを説明できる
  • 引数のあるメソッドを作成できる
  • クラスを別ファイルに分けて書ける
  • 変数のスコープを説明できる

項目内容
開発環境Visual Studio 2022
プロジェクト種類コンソール アプリ
対象フレームワーク.NET 8
ソリューション名Chapter07
プロジェクト名Ch07_Class

csproj の Nullable は disable に変更してください

プロジェクト作成後、Ch07_Class.csproj を開き、<Nullable>disable</Nullable> に変更してください。

詳しい手順は、第 1 章「1-1 プロジェクトを作成する」を参照してください。


これまでの章では、namespace を次のように書いてきました。

Program.cs
namespace Ch06_Array
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello");
}
}
}

この章からは、次の書き方を使います。

Program.cs
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;)の両方が混在しています。 古いプロジェクトや、自動生成されたファイルではブロック形式がよく使われます。 どちらも読めるようにしておきましょう。

この章以降のサンプルコードは、すべてファイルスコープ形式で書きます。


作業を始める前に、次の内容を確認してください。

  • intstring などの変数を使える
  • if 文・for 文・foreach 文を書ける
  • 配列の基本(宣言、インデックス、Length)を説明できる
  • 第 6 章の内容を Git に提出済みである

オブジェクト指向プログラミングとは、プログラムを 「もの」(オブジェクト) の集まりとして考える方法です。

ここでいう「もの」は、現実世界の物体だけではありません。社員、商品、注文、顧客なども、プログラム上の「もの」として扱えます。

それぞれの「もの」には、情報処理 があります。

社員を例にすると、次のように考えられます。

上半分が 情報(社員番号・氏名・部署)、下半分が 処理(社員情報を表示・部署を変更)です。 このように「情報」と「処理」を 1 つのクラスにまとめると、社員に関するものは社員クラスを見ればすべて分かる 状態になります。

C# では、このようなまとまりを クラス として定義します。 そして、そのクラスから実際に作られたものを オブジェクト(または インスタンス)と呼びます。

用語ひとまずの説明
クラス設計図
オブジェクト設計図から作られた実物
プロパティオブジェクトが持つ情報
メソッドオブジェクトに行わせる処理

設計図と実体の関係を図にすると、次のようになります。

1 つの設計図(クラス)から、何個でも実体(オブジェクト)を作れる のがオブジェクト指向の大きな利点です。 たとえば社員が 100 人いても、Employee クラスは 1 つだけで済みます。


7-2 クラスを使わないとどうなるか

Section titled “7-2 クラスを使わないとどうなるか”

クラスを使わずに、社員 1 人分の情報を変数で扱ってみます。

Program.cs
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 人分ならこれで十分です。


社員が 2 人になると、変数が一気に増えます。

int employeeId1 = 1001;
string employeeName1 = "山田二郎";
string departmentName1 = "総務";
int employeeId2 = 1002;
string employeeName2 = "佐藤昭夫";
string departmentName2 = "営業";

このコードには、次の問題があります。

  • 1 人分の情報がひとまとまりになっていない(12 の番号で人を区別している)
  • 変数名が増えて読みにくい
  • メールアドレスなど項目を追加すると、全員分の変数を追加する必要がある

配列で多少改善できますが、配列同士の対応関係を人間が注意して管理しなければなりません。

int[] employeeIds = { 1001, 1002 };
string[] employeeNames = { "山田二郎", "佐藤昭夫" };
string[] departmentNames = { "総務", "営業" };

employeeIds[0]employeeNames[0]departmentNames[0] が同じ社員」と人間が覚えておく必要があるのは不便です。

そこで、1 人分の情報をひとまとまりにする ためにクラスを使います。


社員情報をまとめて扱うために、Employee クラスを作成します。

Program.cs に次のコードを入力してください。

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
氏名:山田二郎
部署:総務

最後の部分が、社員を表すクラスの定義です。

class Employee
{
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
public string DepartmentName { get; set; }
}

Employee クラスには、次の 3 つの情報を持たせています。

プロパティ意味
EmployeeIdint社員番号
EmployeeNamestring氏名
DepartmentNamestring部署

クラスの中に定義した情報を プロパティ と呼びます。 プロパティは「そのオブジェクトが持っている情報」のことです。

補足:プロパティ名はパスカルケース

C# の慣習では、プロパティ名は 先頭を大文字 にする「パスカルケース」を使います。 変数名(キャメルケース、employeeId)とは違うルールです。 どちらも単語の区切りが分かりやすくなるための慣習です。


プロパティの定義には { get; set; } という記号が付いています。

public int EmployeeId { get; set; }

この章では、次のように覚えてください。

記号意味
getプロパティの値を取り出せるようにする
setプロパティに値を入れられるようにする

両方付けると、値の 取り出し代入 の両方ができるプロパティになります。 プロパティの内部的な仕組みや、get だけにして読み取り専用にする書き方などは、第 10 章「クラスについて掘り下げる」 で詳しく扱います。


Main メソッドの先頭で、Employee クラスからオブジェクトを作成しています。

Employee employee = new Employee();

new Employee() によって、Employee クラスをもとにした実際のオブジェクトが作られます。

Employee クラス(設計図)
│ new
employee オブジェクト(実体)

Employee employee の部分は、これまでに見慣れた変数宣言と同じ形です。 ただし型が intstring ではなく、自分で作った クラス名 になっています。


作成したオブジェクトのプロパティには、次のように値を代入できます。

employee.EmployeeId = 1001;
employee.EmployeeName = "山田二郎";
employee.DepartmentName = "総務";

オブジェクト名.プロパティ名 という書き方で、そのオブジェクト固有の情報を扱います。 プロパティへの代入は、通常の変数への代入とほぼ同じ感覚で書けます。


プロパティの値は、変数と同じように参照できます。

Console.WriteLine($"社員番号:{employee.EmployeeId}");
Console.WriteLine($"氏名:{employee.EmployeeName}");
Console.WriteLine($"部署:{employee.DepartmentName}");

文字列補間 $"..." の中でも、{オブジェクト名.プロパティ名} で値を取り出せます。


7-4 複数のオブジェクトを作る

Section titled “7-4 複数のオブジェクトを作る”

クラスを使う最大のメリットは、同じ設計図から複数の実体を作れる ことです。

次のコードを入力して実行してください。

Program.cs
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 佐藤昭夫 営業

employee1employee2 は、同じ Employee クラスから作られていますが、それぞれ別の値を持っています。


プロパティへの代入は、オブジェクトを作るときにまとめて書くこともできます。

Employee employee = new Employee
{
EmployeeId = 1001,
EmployeeName = "山田二郎",
DepartmentName = "総務"
};

これを オブジェクト初期化子 と呼びます。 new クラス名()() の代わりに { } を書き、その中に「プロパティ名 = 値」をカンマ区切りで並べます。

通常の代入と比べて、次のように見やすくなります。

書き方行数特徴
1 行ずつ代入多い順番に書く
オブジェクト初期化子1 ブロックにまとまる何を作っているかが一目で分かる

以後のサンプルでは、オブジェクト初期化子を積極的に使います。


これまでに登場した用語を整理します。

用語意味
クラス設計図Employee
オブジェクト設計図から作られた実体employee1
インスタンスオブジェクトとほぼ同じ意味new Employee() で作られたもの
プロパティオブジェクトが持つ情報EmployeeName
new設計図から実体を作る命令new Employee()

「オブジェクト」と「インスタンス」は、この章ではほぼ同じ意味として扱って構いません。


メソッドは、処理をまとめたものです。 これまでにも、次のようなメソッドを 呼び出して きました。

Console.WriteLine("こんにちは");
int.Parse(input);

これらは C# や .NET が用意しているメソッドです。 この章では、自分でクラスの中にメソッドを定義 します。


社員情報を表示する処理を、Employee クラスの中にまとめます。

Program.cs
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 行 に短くなりました。


PrintProfile メソッドの定義を分解します。

public void PrintProfile()
{
Console.WriteLine($"社員番号:{EmployeeId}");
Console.WriteLine($"氏名:{EmployeeName}");
Console.WriteLine($"部署:{DepartmentName}");
}
部分意味
publicクラスの外から呼び出せる
void戻り値がない(値を返さない)
PrintProfileメソッド名
()引数(後述)
{ }処理の内容

メソッドの中では、同じクラスのプロパティ(EmployeeId など)を クラス名を付けずに 直接書けます。


メソッドを使うには、次のように呼び出します。

employee.PrintProfile();

これは「employee オブジェクトに対して、自分のプロフィールを表示してください」とお願いするイメージです。 プロパティと違って、メソッドの呼び出しでは 必ず () を付けます

補足:プロパティとメソッドの見分け方

第 6 章で配列の Length(プロパティ)と GetLength()(メソッド)の違いに触れました。 一般に、() が付くものがメソッド、付かないものがプロパティです。

種類見方
プロパティemployee.EmployeeName情報を参照している
メソッドemployee.PrintProfile()処理を呼び出している

メソッドは、処理結果を呼び出し元に返す こともできます。 これを 戻り値(返り値)といいます。

次の例では、社員情報を文字列として返す GetProfileText メソッドを作成します。

Program.cs
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:山田二郎(総務)

戻り値のあるメソッドでは、次の 2 つを書きます。

  1. メソッド名の前に 戻り値の型 を書く(stringint など)
  2. メソッドの中で return 値; と書く
public string GetProfileText() // ← 戻り値の型は string
{
return $"{EmployeeId}:..."; // ← この値が呼び出し元に返る
}

void と戻り値ありのメソッドを並べると、違いがはっきりします。

種類戻り値の型return使い方
void メソッドvoid不要(または return;)employee.PrintProfile();
戻り値ありメソッドstringint など必要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 か所を直すだけで済みます。「処理を小さなメソッドに分け、メソッドから別のメソッドを呼ぶ」のは、プログラムを整理する基本的なやり方です。


メソッドには、外部から値を渡す こともできます。 これを 引数 といいます。

次の例では、部署名を変更する ChangeDepartment メソッドを作成します。

Program.cs
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;
}
}

実行結果:

変更前:総務
変更後:開発

引数は、メソッド名の () の中に「型 変数名」で書きます。

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("佐藤昭夫", "営業");

Employee 以外の例として、売上情報を表す Sale クラスを作ります。

Program.cs
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 型の値として返しています。


メソッドを使うことで、次のメリットが得られます。

メリット内容
同じ処理を書かなくてよい表示処理を 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 Employee

Visual Studio で新しいクラスファイルを追加する手順は、次のとおりです。

  1. ソリューションエクスプローラーで、プロジェクト名 Ch07_Class を右クリック
  2. 追加クラス を選択
  3. 名前に Employee.cs と入力して 追加 をクリック

Employee.cs が作成されると、次のようなファイルが自動で出来上がります。

Employee.cs
namespace Ch07_Class
{
internal class Employee
{
}
}

Visual Studio が自動生成するファイルでは、ブロック形式の namespace になっています。 この章ではファイルスコープ形式に統一するため、次のように書き直します。

Employee.cs
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 側からは、これまでと同じように Employee クラスを使えます。

Program.cs
namespace Ch07_Class;
internal class Program
{
static void Main(string[] args)
{
Employee employee = new Employee
{
EmployeeId = 1001,
EmployeeName = "山田二郎",
DepartmentName = "総務"
};
employee.PrintProfile();
}
}

Program.csEmployee.cs の namespace を揃えておけば、using を書かなくても互いに参照できます。 今後この研修では、自作クラスはこの方法で別ファイルに分けて書くことを基本とします。


スコープ とは、変数を使える範囲のことです。 C# では、変数は宣言した { } の中で使えます。

Program.cs
namespace Ch07_Class;
internal class Program
{
static void Main(string[] args)
{
int number = 10; // ← Main メソッド内で宣言
Console.WriteLine(number); // ← OK(同じ {} の中)
}
}

numberMain メソッドの { } の中で宣言されているので、その中だけで使えます。


宣言場所使える範囲
メソッドの中そのメソッドの中だけ
if 文の { } の中その if 文の中だけ
for 文の ( ) の中(カウンタ変数)その for 文の中だけ
クラスの中(プロパティ)そのクラスのすべてのメソッド

if 文の中で宣言した変数を外で使えない

Section titled “if 文の中で宣言した変数を外で使えない”

次のコードはエラーになります。

int score = 80;
if (score >= 60)
{
string result = "合格"; // ← if の中で宣言
}
Console.WriteLine(result); // ← エラー:result は使えない

resultif 文の { } の中で宣言されているため、外では使えません。 外でも使いたい場合は、外側で宣言します。

int score = 80;
string result; // ← 外で宣言
if (score >= 60)
{
result = "合格";
}
else
{
result = "不合格";
}
Console.WriteLine(result); // ← OK

メソッドごとに変数のスコープは独立します。

static について(この章では深追いしません)

下のサンプルでは、Program クラスの中に static void PrintNumber() というメソッドを追加しています。 7-5 で学んだ public void PrintProfile()(Employee クラスのメソッド)とは違い、static という修飾子が付いています。

ここでは「Main メソッドと同じクラス内に書く補助メソッドには static を付ける」とだけ覚えておけば十分です。 static の意味そのものは 次章「第 8 章 静的メソッド/静的プロパティ/静的クラス」 で詳しく学習します。

Program.cs
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);
}
}

実行結果:

10
123
10

Main メソッドの numberPrintNumber メソッドの number は、同じ名前でも別の変数です。


スコープを理解すると、次のミスを減らせます。

  • name が見つかりません」というエラー
  • 同じ名前の変数を、つい同じものだと思ってしまうミス
  • 必要以上に広い範囲で変数を使ってしまうミス

変数は 必要な範囲だけで使えるようにする のが基本です。


第 7 章まで来て、クラス・メソッド・複数ファイルと、コードの構造が一気に増えてきました。コードが複雑になるほど、コメント で意図を書き残しておく価値が高まります。第 1 章で // の書き方は学びましたが、ここで「どう書くとよいか」を簡単に整理します。

コメントは誰のためのものか。 コメントは、後でコードを読む人 ── 数か月後の自分や、一緒に開発するチームの人 ── への手紙です。書いた直後は当たり前に思えても、時間が経つと自分でも忘れます。

「なぜ」を書く ── 意図コメント。 コードを見れば分かる「何をしているか」よりも、「なぜそうするのか」「ぱっと見では分からない意図」を書いたコメントを、ここでは 意図コメント と呼びます。意図コメントは、実務でも研修でも役立つ良い習慣です。

// 退職者は一覧に出さないため、在籍者だけを数える

逆に、コードを読めばすぐ分かることを、そのまま日本語にしただけのコメントは、あまり意味がありません。

total = total + price; // total に price を足す

このコメントは、コードを言い換えているだけで、読む人に新しい情報を与えていません。

気をつけたいことが 2 つあります。

注意点理由
書きすぎないすべての行にコメントを付けると、かえって読みにくくなる
コメントを古いままにしないコードを直したらコメントも直す。中身と食い違ったコメントは、無いより危険(読む人をだましてしまう)

プログラムの先頭に「ヘッダーコメント」を付ける。 ファイルの先頭に、そのプログラムの概要を数行で書いておくと、開いてすぐ何のコードか分かります。この研修の演習課題では、Program.cs の先頭に「課題番号」「プログラムの目的」「自分の氏名」を書きます。

// 課題7-1 Employee クラスの作成
// 社員1人分の情報をクラスにまとめて表示するプログラム
// 氏名:山田二郎

研修のルールと実務の違い

ヘッダーコメントに氏名や課題番号を書くのは この研修のルール です。講師が「誰の・どの課題か」を確認するためで、フォルダのコピー提出とも合わせています。 実務では、ファイル先頭コメントの書き方はプロジェクトごとに異なります。氏名は変更履歴(Git など)で分かるため書かないことも多く、配属先のルールに従うのが基本です。

クラスやメソッドには「役割」を一言。 この章で学んだクラスやメソッドは、上に「何のためのものか」を 1 行添えておくと、他の人がすぐ理解できます。

// 社員 1 人分の情報をまとめて扱うクラス
class Employee
{
...
}

この章以降の演習課題では、ヘッダーコメント と、主要な処理への 意図コメント を必ず入れます。

実務では、コメントは必要なところに絞るのが基本です。ただしこの研修では、コメントも評価の対象としています。特に必須課題は易しいぶん、コメントを書く練習の場でもあります。学習の段階なので、自分の考えや意図を、普段より少し丁寧にコメントで残して構いません。大切なのは行数ではなく、「なぜそうしたか」が読み手に伝わることです。


つまずき原因対応
クラスとオブジェクトの違いが分からない設計図と実体の区別が曖昧クラスは設計図、オブジェクトはそこから作られた実体
new Employee() の意味が分からないクラスからオブジェクトを作る処理に慣れていないnew は「設計図から実物を作る命令」と考える
プロパティと変数の違いが曖昧オブジェクトに属するかどうかを意識していないemployee.Name のように オブジェクト名. が付くのがプロパティ
メソッド呼び出しで () を忘れるプロパティと混同しているメソッドには必ず () が付く
return を書き忘れる戻り値のあるメソッドの理解が不十分戻り値の型が void 以外なら return が必要
void の意味が分からない戻り値の有無を意識していないvoid は「値を返さない」
クラスの中に書く場所が分からないProgram クラスと自作クラスの位置関係に慣れていない別ファイル(Employee.cs)を作るのが基本
変数が使えないエラーが出るスコープの範囲外で使っている変数を宣言した { } を確認する
if の中で宣言した変数が外で使えないブロック内だけ有効な変数になっている外でも使うなら外側で宣言する
namespace のセミコロンを忘れるファイルスコープ形式に慣れていないnamespace XXX; のように末尾に ; を付ける

次の項目について、自分で説明できるか確認してください。

  • オブジェクト指向の考え方をおおまかに説明できる
  • クラスとオブジェクトの違いを説明できる
  • クラスにプロパティを定義できる
  • new を使ってオブジェクトを作成できる
  • オブジェクト初期化子を使える
  • クラスにメソッドを定義できる
  • void と戻り値ありメソッドの違いを説明できる
  • 引数のあるメソッドを作成できる
  • クラスを別ファイルに分けて書ける
  • ファイルスコープ namespace の書き方を説明できる
  • スコープとは何かを説明できる

研修の進め方によっては、隣の人またはチーム内で説明確認を行います。

次の内容を、自分の言葉で説明してください。

  1. クラスとオブジェクトの違いは何ですか。
  2. new Employee() は何をしているコードですか。
  3. プロパティとメソッドの違いは何ですか。
  4. void はどのような意味ですか。
  5. return は何のために使いますか。
  6. 引数とは何ですか。
  7. クラスを別ファイルに分けると、どんなメリットがありますか。
  8. スコープとは何ですか。

説明するときは、完全な答えでなくても構いません。 自分の言葉で説明しようとすることが大切です。


ここまでに学んだ クラス・プロパティ・メソッド を、講義の中で実際に手を動かして確認します。オブジェクト指向は、読むだけでは身につきにくいためです。

次の 3 問は、講師と一緒に進める 講義中の演習 です。講義用ソリューション Chapter07(プロジェクト Ch07_Class)の中で作成します。提出はしません(提出する課題は、後の「演習課題」です)。


課題 7-1 Employee クラスを作成する

Section titled “課題 7-1 Employee クラスを作成する”

社員情報を表す Employee クラスを別ファイル(Employee.cs)に作成し、Main メソッドで 1 人分のデータを表示してください。

Employee クラスには、次のプロパティを持たせます。

プロパティ名意味
EmployeeIdint社員番号
EmployeeNamestring氏名
DepartmentNamestring部署

実行結果例:

社員番号:1001
氏名:山田二郎
部署:総務

条件:

  • Employee クラスは Employee.cs に書く
  • オブジェクト初期化子で値を設定する
  • Program.csMain メソッドで表示する

課題 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「演習の進め方」 を参照してください。


課題はソリューション Kadai07 の中に作成してください。 課題ごとに別のプロジェクトを作成 し、指定されたプロジェクト名を使います。

課題プロジェクト名作成する主なファイル
課題 7-1aKd07_01a_ProductClassProgram.csProduct.cs
課題 7-2aKd07_02a_PrintInfoProgram.csProduct.cs
課題 7-3aKd07_03a_MultipleProductsProgram.csProduct.cs
課題 7-4Kd07_04_BankAccountProgram.csBankAccount.cs
課題 7-5Kd07_05_SalaryGradeProgram.csEmployee.cs
課題 7-6Kd07_06_TopSalaryProgram.csEmployee.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 を右クリックし、追加新しいプロジェクト から追加します。


以下は、本章のすべての課題に共通する作業です。各課題の本文には繰り返し書きません。

作業内容参照
プロジェクト作成時の設定最上位レベルのステートメントを使用しない」にチェック第 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 addgit commitgit 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 というテキストファイルを提出先に作成します。コピーが終わってから、次の手順で作成してください。

  1. Kadai07 フォルダをコピーした、サーバ上の自分の名前のフォルダをエクスプローラーで開く
  2. 何もないところで右クリック → 新規作成 → テキスト ドキュメント を選ぶ
  3. ファイル名を 提出メモ.txt に変更する
  4. ダブルクリックで開き、次の内容を書いて保存する
どこまで完成: 7-1a〜7-3a完成、発展未着手
詰まったポイント: 特になし

書く内容は、コミットメッセージに書くはずだった「どこまで完成したか」「詰まったポイント」と同じです。順調なときも「詰まったポイント: 特になし」と明示 してください。

タイマー後のチーム時間の使い方

タイマー後、講師が指定する 発表開始時刻 までがチーム時間です。チーム内で次の 3 つを自由に管理してください。

  • コードレビュー:他のメンバーの commit を読んで、気づいたことを共有する
  • 発表者の選出:発表開始時刻に、チームから 1 人が要点を発表できるよう、誰が話すかを決めておく
  • 実装の続行(任意):途中だった課題を続けても構いません(提出後の追加分は評価対象外ですが、理解を深める意義はあります)

どの順番で進めるか、何分ずつ使うかはチームの判断です。ただし 発表開始時刻は厳守 です。その時刻までにレビューと発表者選出を必ず終わらせてください。


まずは、全員が必須課題に取り組んでください。講義中演習課題と似た構成ですが、別の題材(商品)で取り組みます。


課題 7-1a Product クラスを作成する

Section titled “課題 7-1a Product クラスを作成する”

商品情報を表す Product クラスを別ファイル(Product.cs)に作成し、Main メソッドで 1 つ分のデータを表示してください。

Product クラスには、次のプロパティを持たせます。

プロパティ名意味
ProductIdint商品番号
ProductNamestring商品名
Priceint価格

実行結果例:

商品番号:101
商品名:ノート
価格:180円

条件:

  • Product クラスは Product.cs に書く
  • オブジェクト初期化子で値を設定する
  • Program.csMain メソッドで表示する

課題 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):Balanceamount を加算する(戻り値 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 で配列をまわして、より高い給与が見つかったら更新します。


  • プログラムを Visual Studio から実行できる
  • クラスを別ファイル(Employee.cs など)に書けている
  • namespace XXX; のファイルスコープ形式で書けている
  • クラスにプロパティを定義できている
  • new を使ってオブジェクトを作成できている
  • オブジェクト初期化子を使えている
  • クラスにメソッドを定義できている
  • 戻り値のあるメソッドで return を使えている
  • 引数のあるメソッドを作成できている
  • インデントが整っている
  • 課題を提出した(Git の commitpush、または講師指示があれば Kadai07 フォルダをサーバへコピーし、提出先フォルダに 提出メモ.txt を作成)

タイマーが鳴ったら、第 7 章の課題プロジェクト群(Kadai07 ソリューション)をその場で Git に提出します。

Terminal window
git status
git add .
git commit -m "Chapter07 タイマー提出: <どこまで完成> / <詰まったポイント>"
git push

実行例:

Terminal window
git commit -m "Chapter07 タイマー提出: 7-1a〜7-2a完成、7-3aは途中 / 7-3a の 2 つ目のオブジェクトの作り方で悩んだ"

Git の詳しい操作は、付録 C「Git のインストールと提出ルール」 を参照してください。

コミットメッセージの形式は、本章冒頭「演習課題 > 提出ルール(タイマー方式)」を参照してください。

サーバへのコピーで提出する場合

講師から指示があった場合は、push の代わりに Kadai07 フォルダをサーバへコピーして提出します。コピー後、提出先(サーバ上の自分の名前のフォルダ)をエクスプローラーで開き、右クリック → 新規作成 → テキスト ドキュメント で 提出メモ.txt を作って「どこまで完成」「詰まったポイント」を書いてください。詳しくは本章冒頭「演習課題 > 提出方法:Git が使えないときはサーバへコピー」を参照してください。


この章では、クラスとオブジェクト指向プログラミングの基礎を学習しました。

主な内容は次のとおりです。

  • オブジェクト指向では、関連する情報と処理を 1 つのまとまりとして考える
  • クラスはオブジェクトを作るための設計図、オブジェクトはそこから作られた実体
  • プロパティはオブジェクトが持つ情報、メソッドはオブジェクトが行う処理
  • new を使うとクラスからオブジェクトを作成できる
  • オブジェクト初期化子を使うと、プロパティの値をまとめて設定できる
  • void は値を返さないメソッド、戻り値のあるメソッドでは return を使う
  • メソッドには引数を渡して外部から値を受け取れる
  • クラスは別ファイルに分けて書くのが基本(Employee.cs のように)
  • この章から、namespace XXX; のファイルスコープ形式を使う
  • 変数には使える範囲(スコープ)があり、宣言した { } の中で使える

次章では、静的メソッド・静的プロパティ・静的クラス(static)を学習します。

この章では、new Employee() でオブジェクトを作ってからプロパティやメソッドを使いました。 次章では、Console.WriteLineint.Parse のように、オブジェクトを作らずに呼び出せる機能 について学習します。

// この章で学んだ書き方
Employee employee = new Employee();
employee.PrintProfile();
// 次章で学ぶ書き方
Console.WriteLine("Hello"); // ← Console オブジェクトを new していない
int n = int.Parse("123"); // ← int オブジェクトを new していない

「クラス名.メソッド名()」という見覚えのある書き方の仕組みが、次の章で分かります。