Skip to content

第11章 値型と参照型

この章では、C# の型を理解するうえで重要な 値型参照型 について学習します。

これまでに、次のような型を使ってきました。

int score = 80;
bool isPassed = true;
string employeeName = "山田二郎";
Employee employee = new Employee(1001, "山田二郎", "総務");

一見、どれも同じように「変数に値を入れている」ように見えます。 しかし、C# では型によって 値の扱われ方 が異なります。

値型
値そのものを変数が持つ
参照型
オブジェクトの「場所」を変数が指し示す

この違いは、後のオブジェクト指向や DB 接続で重要になります。 特に、データベースから取得したデータには NULL が混じることがあり、その NULL を C# 側でどう扱うかが課題になります。

この章で学ぶ内容:

構造体(struct)
列挙型(enum)
値型と参照型の違い
null とは何か
null許容型(int?、decimal? など)

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

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

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

  • 値型と参照型の違いをおおまかに説明できる
  • 構造体とは何かを説明できる
  • 簡単な構造体を定義できる
  • 列挙型とは何かを説明できる
  • 列挙型を使って選択肢を分かりやすく表現できる
  • 値型を別の変数に代入したときの動きを説明できる
  • 参照型を別の変数に代入したときの動きを説明できる
  • メソッドに値型・参照型を渡したときの違いを説明できる
  • null が「参照先がない」状態を表すことを説明できる
  • null によるエラーを防ぐ書き方ができる
  • int?decimal? のような null 許容値型を使える
  • DB の NULL と C# の null の関係をおおまかに説明できる

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

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

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

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


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

  • intdoubleboolstring を使える
  • クラス・プロパティ・コンストラクター・メソッドを書ける(第 7〜10 章)
  • if 文・switch 文で条件分岐できる
  • 第 10 章の内容を Git に提出済みである

構造体(struct)は、種類の違うデータを 1 つにまとめて扱えるようにした 型です。 たとえば「名前(string)」と「年齢(int)」のように、別々の型の値を 1 つのまとまりとして持てます。 クラスと似ていますが、値型(代入すると値そのものがコピーされる)であることが大きな違いです。

実は、これまで使ってきた次の型も、すべて構造体の仲間です。

用途
intdoubledecimal数値
bool真偽値
char1 文字
DateTime日付・時刻

intbool のように 1 つの値だけを持つシンプルな構造体もあれば、DateTime のように複数のデータ(年・月・日・時・分・秒)をまとめた構造体もあります。 つまり、皆さんはすでに構造体を毎日使っています。


社員番号を「部門記号(string)」と「連番(int)」の 2 つで表す構造体を作ってみます。 型の違う 2 つのデータ(stringint)を 1 つにまとめている 点に注目してください。これが構造体らしい使い方です。

EmployeeId.cs:

EmployeeId.cs
namespace Ch11_ValueReferenceNull;
struct EmployeeId
{
public string Prefix { get; set; } // 部門記号(例:EMP)
public int Number { get; set; } // 連番(例:1001)
public EmployeeId(string prefix, int number)
{
Prefix = prefix;
Number = number;
}
public string ToFullId()
{
return $"{Prefix}{Number}";
}
}

Program.cs:

Program.cs
namespace Ch11_ValueReferenceNull;
internal class Program
{
static void Main(string[] args)
{
EmployeeId id = new EmployeeId("EMP", 1001);
Console.WriteLine($"部門記号:{id.Prefix}");
Console.WriteLine($"連番:{id.Number}");
Console.WriteLine($"社員番号:{id.ToFullId()}");
}
}

実行結果:

部門記号:EMP
連番:1001
社員番号:EMP1001

構造体は struct キーワードで定義します。Prefix(string)と Number(int)という 型の違うデータを 1 つにまとめToFullId() のようなメソッドも持てます。プロパティ・コンストラクター・メソッドを持てる点はクラスと同じです。


観点構造体(struct)クラス(class)
型の分類値型参照型
代入時値そのものがコピーされる参照(場所)がコピーされる
用途小さな値のまとまり(座標、ID、日付など)機能や状態を持つもの(社員、商品、注文など)

この章では、構造体は 「小さな値のまとまりを表す型」 くらいの理解で十分です。 業務で自分から構造体を作る場面は多くありませんが、DateTime のような既製の構造体は頻繁に使います。


列挙型(enum)は、決まった選択肢を名前で表す ための型です。

たとえば、社員の在籍状態を文字列で管理すると、表記揺れが起きやすくなります。

string status1 = "在籍中";
string status2 = "在籍"; // ← 表記違い
string status3 = "在籍中 "; // ← 末尾に空白

if (status == "在籍中") で判定するつもりが、別の表記が混ざってマッチしない、ということが起きます。 選択肢が決まっている 場合は、列挙型を使うと安全です。


EmployeeStatus.cs:

EmployeeStatus.cs
namespace Ch11_ValueReferenceNull;
enum EmployeeStatus
{
Active, // 在籍中
Retired, // 退職
LeaveOfAbsence // 休職中
}

Program.cs:

Program.cs
namespace Ch11_ValueReferenceNull;
internal class Program
{
static void Main(string[] args)
{
EmployeeStatus status = EmployeeStatus.Active;
if (status == EmployeeStatus.Active)
{
Console.WriteLine("在籍中です。");
}
else
{
Console.WriteLine("在籍中ではありません。");
}
}
}

実行結果:

在籍中です。

EmployeeStatus.Active のように クラス名.値 の形でアクセスします。 ActiveRetiredLeaveOfAbsence 以外の値を代入しようとするとコンパイルエラーになるため、決まった選択肢だけが扱われます。


列挙型は switch 文と相性がよいです。

Program.cs
namespace Ch11_ValueReferenceNull;
internal class Program
{
static void Main(string[] args)
{
EmployeeStatus status = EmployeeStatus.LeaveOfAbsence;
switch (status)
{
case EmployeeStatus.Active:
Console.WriteLine("在籍中です。");
break;
case EmployeeStatus.Retired:
Console.WriteLine("退職しています。");
break;
case EmployeeStatus.LeaveOfAbsence:
Console.WriteLine("休職中です。");
break;
}
}
}

実行結果:

休職中です。

case の値も列挙型なので、入力ミスがあればコンパイル時に気付けます。


列挙型を表示すると英語名になる

Section titled “列挙型を表示すると英語名になる”

列挙型の値をそのまま Console.WriteLine に渡すと、英語名 が出力されます。

Console.WriteLine(EmployeeStatus.Active); // → Active

画面表示には日本語が必要なことが多いので、変換用のメソッドを用意します。 たとえば Employee クラスに Status プロパティを持たせ、表示用文字列を返すメソッドを足します。

Employee.cs:

Employee.cs
namespace Ch11_ValueReferenceNull;
class Employee
{
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
public EmployeeStatus Status { get; set; }
public Employee(int employeeId, string employeeName, EmployeeStatus status)
{
EmployeeId = employeeId;
EmployeeName = employeeName;
Status = status;
}
public string GetStatusText()
{
switch (Status)
{
case EmployeeStatus.Active:
return "在籍中";
case EmployeeStatus.Retired:
return "退職";
case EmployeeStatus.LeaveOfAbsence:
return "休職中";
default:
return "不明";
}
}
}

Program.cs:

Program.cs
namespace Ch11_ValueReferenceNull;
internal class Program
{
static void Main(string[] args)
{
Employee employee = new Employee(1001, "山田二郎", EmployeeStatus.Active);
Console.WriteLine($"社員番号:{employee.EmployeeId}");
Console.WriteLine($"氏名:{employee.EmployeeName}");
Console.WriteLine($"状態:{employee.GetStatusText()}");
}
}

実行結果:

社員番号:1001
氏名:山田二郎
状態:在籍中

11-3 値型と参照型の動きの違い

Section titled “11-3 値型と参照型の動きの違い”

C# の型は、大きく次のように分けられます。

種類代表例値の扱い方
値型intdoubleboolDateTime、構造体、列挙型値そのものを変数が持つ
参照型string、配列、クラス(自作含む)、List<T>オブジェクトの場所を変数が指す

この違いは、変数を別の変数に代入したときに最もはっきり現れます。

補足:string は特別な参照型

string は参照型ですが、文字列を変更すると常に新しい文字列が作られるため、値型のように振る舞います。 この章では深追いせず、「string は普段は値型のように扱えば OK」と覚えておきましょう。


int 型で代入の動きを確認します。

Program.cs
namespace Ch11_ValueReferenceNull;
internal class Program
{
static void Main(string[] args)
{
int numberA = 10;
int numberB = numberA; // numberA の値 10 をコピー
numberA = 99; // numberA だけ変更
Console.WriteLine($"numberA:{numberA}");
Console.WriteLine($"numberB:{numberB}");
}
}

実行結果:

numberA:99
numberB:10

int numberB = numberA; の時点で 値 10 がコピー されます。 その後 numberA を変更しても、numberB には影響しません。

numberA = 10 → numberB = 10 (10 をコピー)
numberA = 99 → numberB = 10 (独立しているのでそのまま)

同じことをクラスで試します。

Employee.cs(シンプル版):

Employee.cs
namespace Ch11_ValueReferenceNull;
class Employee
{
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
public Employee(int employeeId, string employeeName)
{
EmployeeId = employeeId;
EmployeeName = employeeName;
}
}

Program.cs:

Program.cs
namespace Ch11_ValueReferenceNull;
internal class Program
{
static void Main(string[] args)
{
Employee employeeA = new Employee(1001, "山田二郎");
Employee employeeB = employeeA; // 同じオブジェクトを共有
employeeA.EmployeeName = "佐藤昭夫";
Console.WriteLine($"employeeA:{employeeA.EmployeeName}");
Console.WriteLine($"employeeB:{employeeB.EmployeeName}");
}
}

実行結果:

employeeA:佐藤昭夫
employeeB:佐藤昭夫

Employee employeeB = employeeA; では、オブジェクトの コピー ではなく、オブジェクトの 場所(参照) がコピーされます。 つまり、employeeAemployeeB同じ 1 つのオブジェクト を別の名前で見ている状態です。

  • 矢印は 「変数が指している先」 を表します
  • employeeAemployeeB の 2 本の矢印が 同じ 1 つのオブジェクト に向いている状態です
  • どちらの変数経由でも、同じオブジェクトに変更を加えることになります(=だから片方を変えるともう片方にも見える)

別々のオブジェクトにしたいときは new

Section titled “別々のオブジェクトにしたいときは new”

別のオブジェクトとして扱いたい場合は、それぞれ new します。

Employee employeeA = new Employee(1001, "山田二郎");
Employee employeeB = new Employee(1001, "山田二郎"); // 別オブジェクト
employeeA.EmployeeName = "佐藤昭夫";
Console.WriteLine($"employeeA:{employeeA.EmployeeName}");
Console.WriteLine($"employeeB:{employeeB.EmployeeName}");

実行結果:

employeeA:佐藤昭夫
employeeB:山田二郎

中身が同じ値でも、new で別々に作ったオブジェクトは 別物 です。


値型をメソッドに渡したときも、値はコピーされます。

Program.cs
namespace Ch11_ValueReferenceNull;
internal class Program
{
static void Main(string[] args)
{
int number = 10;
ChangeNumber(number);
Console.WriteLine(number);
}
static void ChangeNumber(int value)
{
value = 99;
}
}

実行結果:

10

メソッド内で value = 99; としても、呼び出し元の number には影響しません。


一方、参照型では呼び出し元のオブジェクトに 影響します

Program.cs
namespace Ch11_ValueReferenceNull;
internal class Program
{
static void Main(string[] args)
{
Employee employee = new Employee(1001, "山田二郎");
ChangeEmployeeName(employee);
Console.WriteLine(employee.EmployeeName);
}
static void ChangeEmployeeName(Employee target)
{
target.EmployeeName = "佐藤昭夫";
}
}

実行結果:

佐藤昭夫

targetemployee は同じオブジェクトを指しているため、target.EmployeeName を変えると、呼び出し元の employee.EmployeeName も変わって見えます。


観点値型参照型
代表例intboolDateTime、構造体クラス、配列、List<T>
代入で何がコピーされる?値そのもの参照(場所)
別の変数への変更の影響影響しない同じオブジェクトを指していれば影響する
メソッド呼び出し時の挙動値がコピーされて渡される参照がコピーされて渡される(中身は共有)
null を入れられる?通常入れられない入れられる

11-4 参照先がないことを表す null

Section titled “11-4 参照先がないことを表す null”

null は、参照型の変数が 何のオブジェクトも指していない 状態を表す特別な値です。

Employee employee = null;

employee という変数はありますが、まだ具体的な Employee オブジェクトを指していません。


null の変数を使うとエラーになる

Section titled “null の変数を使うとエラーになる”

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

Employee employee = null;
Console.WriteLine(employee.EmployeeName); // ← 実行時エラー

employee は何も指していないので、.EmployeeName を取り出そうとしても対象が存在しません。 このエラーは NullReferenceException という名前で、C# でもっともよく遭遇するエラーの 1 つです。

employee がオブジェクトを指していない
employee.EmployeeName を取ろうとする
そもそも EmployeeName を持つ「もの」がない
NullReferenceException

null の可能性がある変数は、使う前に if で確認します。

Program.cs
namespace Ch11_ValueReferenceNull;
internal class Program
{
static void Main(string[] args)
{
Employee employee = FindEmployee(9999);
if (employee == null)
{
Console.WriteLine("社員が見つかりませんでした。");
}
else
{
Console.WriteLine($"見つかりました:{employee.EmployeeName}");
}
}
static Employee FindEmployee(int employeeId)
{
if (employeeId == 1001)
{
return new Employee(1001, "山田二郎");
}
return null; // 見つからなかった場合
}
}

実行結果:

社員が見つかりませんでした。

「検索結果がないこと」を null で表すのは、C# でよくあるパターンです。

補足:null になり得るメソッドの目印

Nullable enable が有効なプロジェクトでは、null を返す可能性があるメソッドの戻り値を Employee? のように ? 付きで宣言します。 本研修は Nullable disable のため ? は使いませんが、現場のコードで Employee? を見たら「null になるかもしれない」というサインだと読み取りましょう。


値型には通常 null を入れられない

Section titled “値型には通常 null を入れられない”

intDateTime などの値型には、通常 null を代入できません。

int salary = null; // ← コンパイルエラー
DateTime hireDate = null; // ← コンパイルエラー

値型は「常に何らかの値を持つ」型だからです。


値型でも、? を付けると null を入れられる型 になります。

int? salary = null;
decimal? commission = null;
DateTime? hireDate = null;
bool? isManager = null;

これを null 許容値型 と呼びます。

通常の値型null 許容値型
intint?
decimaldecimal?
DateTimeDateTime?
boolbool?

int? は内部的には「int の値、または『値なし』」のどちらかを保持する仕組みです。


Program.cs
namespace Ch11_ValueReferenceNull;
internal class Program
{
static void Main(string[] args)
{
int? salary = null;
if (salary == null)
{
Console.WriteLine("給与は未設定です。");
}
else
{
Console.WriteLine($"給与:{salary}");
}
}
}

実行結果:

給与は未設定です。

int? を使うことで、値が決まっていない状態 を表現できます。


null 許容値型には、専用のプロパティがあります。

プロパティ内容
HasValue値があれば truenull なら false
Value値を取り出す(null の状態で呼ぶとエラー)
int? salary = 500000;
if (salary.HasValue)
{
Console.WriteLine($"給与:{salary.Value}");
}
else
{
Console.WriteLine("給与は未設定です。");
}

実行結果:

給与:500000円

注意:Value は null のときに呼ぶとエラー

salary.Value は、salarynull の状態で呼び出すと InvalidOperationException になります。 必ず HasValue を確認してから使うか、==null チェックしてから使ってください。


?? を使うと、null だった場合の代わりの値 を簡潔に指定できます。

int? salary = null;
int displaySalary = salary ?? 0;
Console.WriteLine($"表示用給与:{displaySalary}");

実行結果:

表示用給与:0円
salary ?? 0
salary が null でなければ salary の値
salary が null なら 0

if 文と比べて短く書けます。

書き方意味
salary ?? 0salary が null なら 0、それ以外は salary
userName ?? "ゲスト"userName が null なら “ゲスト”
hireDate ?? DateTime.TodayhireDate が null なら今日の日付

データベースでは、値が入っていないことを NULL で表します。 SQL 研修で扱った Oracle の employees テーブルでも、commission(歩合)が NULL の社員がいたはずです。第 16 章以降で扱う SQLServer のテーブルでも、emailsalary のような列は NULL を持てるよう設計されています。

C# で DB のデータを受け取るとき、NULL になりうる列は null 許容値型 で受け取ります。

class Employee
{
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
public int JobId { get; set; }
public int? ManagerId { get; set; } // ← NULL あり
public DateTime HireDate { get; set; }
public decimal Salary { get; set; }
public decimal? Commission { get; set; } // ← NULL あり
public int DepartmentId { get; set; }
}

NULL の可能性がある列を int?decimal?DateTime? で表しておくと、SQL の NULL を C# の null として自然に受け取れます。 詳しい使い方は、第 17 章「C# からデータベースを操作する」以降で扱います。

この章で覚えておきたいのは次のことです。

DB の NULL
→ C# では null として扱う
値型で null を持たせたい
→ int? や decimal? を使う
参照型はそのまま null を持てる
→ 使う前に null チェックする

つまずき原因対応
構造体とクラスの違いが分からない見た目が似ている「構造体 = 値型、クラス = 参照型」
列挙型を使う意味が分からない文字列でも書ける決まった選択肢を安全に扱うために使う
値型代入の動きを間違える値がコピーされる感覚がないint の代入で確認する
参照型代入の動きを間違える同じオブジェクトを指す感覚がないEmployeeName を変えて確認する
メソッド内で値を変えたのに反映されない値型はコピーが渡される値型と参照型の違いを確認する
null の意味が分からない空文字や 0 と混同しているnull は「参照先がない」状態
null の変数を使ってエラーになるオブジェクトがないのにプロパティを呼んでいるif (変数 == null) で先に確認
int salary = null; がエラーint は値型で通常 null を持てないint? を使う
salary.Value でエラーnull のとき Value を呼んでいるHasValue または ?? を先に確認
列挙型の表示が英語のままConsole.WriteLine(status) は英語名を返す変換メソッドで日本語に変える

  • 値型と参照型の違いを説明できる
  • struct で構造体を定義できる
  • DateTime も構造体であることを説明できる
  • enum で列挙型を定義できる
  • 列挙型の値を switch 文で判定できる
  • 列挙型を日本語表示するメソッドを書ける
  • 値型の代入結果を説明できる
  • 参照型の代入結果を説明できる
  • new すると別オブジェクトになることを説明できる
  • メソッドに値型・参照型を渡したときの違いを説明できる
  • null が何を表すか説明できる
  • null チェックを if で書ける
  • int?decimal?DateTime? のような null 許容値型を使える
  • HasValueValue を使える
  • ?? を使って null 時の代替値を指定できる

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

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

  1. 値型と参照型の違いは何ですか。
  2. int numberB = numberA; のあと numberA を変更しても numberB が変わらないのはなぜですか。
  3. Employee employeeB = employeeA; のあと employeeA.EmployeeName を変更すると、employeeB.EmployeeName も変わるのはなぜですか。
  4. null とは何ですか。
  5. intnull を入れたい場合、どう書きますか。
  6. ?? 演算子はどのようなときに使いますか。
  7. DB の NULL を C# で扱うとき、どんな型を使いますか。

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


この章の演習課題に取り組みます。

本章では タイマー方式 を試験導入します。次の 3 段階で進めてください。

段階時間内容
① 準備10 分(目安)上の「ペア確認」と、これから取り組む課題(仕様)の読み込み。ペアや講師に質問してよい
② ソロ作業25 分(タイマーで計測)一人で課題に取り組む。タイマーが鳴ったら、完成・未完成にかかわらず作業を止めて提出する
③ チーム時間講師が指定する発表開始時刻までチーム内でコードレビューを行い、発表者を決める。実装の続行も可。時間配分はチームで管理する

第 11 章の必須課題は 本文サンプルの動きを観察する型 が中心で重くはありませんが、別ファイルでクラス・構造体・列挙型を作る作業が課題ごとに発生します。ソロ作業は 25 分 をとっています。準備フェーズの終わりに講師が号令をかけ、そこからタイマーを開始します。 評価対象はタイマー時点で提出されたコードです(タイマー後に書き足した分は評価には含まれません)。 発表開始時刻は厳守 です。チーム時間中も、その時刻が来たら全員手を止めて発表に移ります。

演習の進め方の詳細は、付録A「演習の進め方」 を参照してください。


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

課題必須/発展プロジェクト名作成する主なファイル
課題 11-1必須Kd11_01_ValueAndReferenceProgram.csEmployee.cs
課題 11-2必須Kd11_02_EmployeeStatusEnumProgram.csEmployeeStatus.cs
課題 11-3必須Kd11_03_FindEmployeeProgram.csEmployee.cs
課題 11-4発展Kd11_04_PeriodDaysProgram.csPeriod.cs
課題 11-5発展Kd11_05_NullableSalaryProgram.cs
課題 11-6発展Kd11_06_EmployeeWithNullProgram.csEmployee.cs

フォルダ構成は次のようになります。

Kadai11/ ← 課題用ソリューションフォルダ
Kadai11.sln
Kd11_01_ValueAndReference/
Kd11_02_EmployeeStatusEnum/
Kd11_03_FindEmployee/
Kd11_04_PeriodDays/
Kd11_05_NullableSalary/
Kd11_06_EmployeeWithNull/

補足:ソリューションに複数のプロジェクトを追加する方法

最初の課題で Kadai11 ソリューションと Kd11_01_ValueAndReference プロジェクトを同時に作成します。

2 つ目以降の課題は、ソリューションエクスプローラーで Kadai11 を右クリックし、追加新しいプロジェクト から追加します。


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

作業内容参照
プロジェクト作成時の設定最上位レベルのステートメントを使用しない」にチェック第 1 章 1-1
csproj の編集<Nullable>disable</Nullable> に変更第 1 章 1-1
namespace の書き方ファイルスコープ形式(namespace XXX;)で書く第 7 章 冒頭
クラス・構造体・列挙型ファイルの追加ソリューションエクスプローラーでプロジェクトを右クリック → 追加クラス第 7 章 7-7
ファイルを保存して実行Ctrl + S で保存 → F5 で実行第 1 章 1-2

特に 2 つ目以降のプロジェクト(Kd11_02_EmployeeStatusEnum など)も、新規追加するたびに csproj の Nullable を disable に変更する 必要があります。

クラスファイルを自動生成すると、ブロック形式の namespace プロジェクト名 { ... } で作られます(例:課題 11-1 なら namespace Kd11_01_ValueAndReference { ... })。本章でもファイルスコープ形式に統一するため、生成後に namespace Kd11_01_ValueAndReference; のように ; 形式に書き換えてください。同じプロジェクト内の Program.cs と他のクラス・構造体・列挙型ファイルの namespace は揃えます。

補足:構造体・列挙型ファイルの追加

Visual Studio の「追加 → クラス」テンプレートで作成すると、生成されるファイルは class で始まります。構造体・列挙型を作る場合は、生成後に class XXX の部分を struct XXX または enum XXX に書き換えてください。

提出ルール(タイマー方式)

25 分のタイマーが鳴ったら手を止め、git addcommitpush を行います。評価対象はタイマー時点のコミットのみです(タイマー後の追加は評価外)。

Chapter11 タイマー提出: <どこまで完成> / <詰まったポイント>

例:Chapter11 タイマー提出: 11-1〜11-2完成、11-3は途中 / null 返却の書き方で詰まった

サーバへコピー提出の場合

Git 不調で講師指示があれば、push の代わりに Kadai11 フォルダをサーバへコピーし、提出先に 提出メモ.txt を作成します(内容:どこまで完成 / 詰まったポイント)。

タイマー後のチーム時間

発表開始時刻まで、コードレビュー / 発表者選出 / 実装続行(任意・評価外)。時間配分はチームの判断、発表開始時刻は厳守


まずは、全員が必須課題に取り組んでください。


課題 11-1 値型と参照型の代入を確認する

Section titled “課題 11-1 値型と参照型の代入を確認する”

値型と参照型の代入の違いを 1 つのプログラムで確認してください。

処理内容

  1. int numberA = 10; を作成、int numberB = numberA; で代入、その後 numberA = 99; に変更
  2. numberAnumberB の値を表示
  3. Employee employeeA = new Employee(1001, "山田二郎"); を作成、Employee employeeB = employeeA; で代入、その後 employeeA.EmployeeName = "佐藤昭夫"; に変更
  4. employeeA.EmployeeNameemployeeB.EmployeeName の値を表示

Employee クラスのプロパティ:

プロパティ名
EmployeeIdint
EmployeeNamestring

Employee.cs を別ファイルとして作成し、引数付きコンストラクターを用意してください。

実行結果例:

[値型]
numberA:99
numberB:10
[参照型]
employeeA:佐藤昭夫
employeeB:佐藤昭夫

条件:

  • 値型と参照型で結果が異なることを実行で確認する
  • Employee クラスは別ファイル Employee.cs に書く

課題 11-2 EmployeeStatus 列挙型を使う

Section titled “課題 11-2 EmployeeStatus 列挙型を使う”

社員の在籍状態を表す列挙型を作成し、switch 文で日本語に変換して表示してください。

EmployeeStatus.cs:

enum EmployeeStatus
{
Active,
Retired,
LeaveOfAbsence
}

Main メソッドの処理:

  • 変数 EmployeeStatus status に 3 つの値を順番に代入する
  • それぞれについて switch 文で日本語表示する

実行結果例:

在籍中です。
退職しています。
休職中です。

条件:

  • enum を使う
  • switch 文を使う
  • 3 つの状態すべてを試す

課題 11-3 null を返すメソッドと null チェック

Section titled “課題 11-3 null を返すメソッドと null チェック”

社員を検索するメソッドを作成し、見つからなかった場合に null を返してください。

仕様

  • メソッド名:FindEmployee(int employeeId)
  • 戻り値:Employee
  • 社員番号 1001 のときだけ new Employee(1001, "山田二郎") を返す
  • それ以外のときは null を返す

Main メソッドの処理

  • FindEmployee(1001)FindEmployee(9999) の両方を呼び出す
  • 結果が null かどうかを if 文で確認し、見つかった場合は氏名を、見つからなかった場合は「社員が見つかりませんでした。」と表示する

実行結果例:

見つかりました:山田二郎
社員が見つかりませんでした。

条件:

  • Employee クラスは別ファイルに書く
  • FindEmployee メソッドは Program.csstatic メソッドとして定義してよい
  • 戻り値を使う前に必ず null チェックする

必須課題が終わった人は、発展課題に取り組んでください。 発展課題からは、仕様だけが提示されます。実装方法は自分で考えてください。


課題 11-4 Period 構造体で勤続日数を計算する

Section titled “課題 11-4 Period 構造体で勤続日数を計算する”

期間を表す Period 構造体を自分で設計し、入社日から今日までの 勤続日数 を計算してください。

DateTime も C# 標準の構造体(=値型)です。本課題ではその DateTime を 2 つ束ねた、自作の構造体を作ります。

構造体仕様

  • ファイル名:Period.cs
  • 構造体名:Period
  • プロパティ:
    • Start (DateTime):開始日
    • End (DateTime):終了日
  • コンストラクター:Period(DateTime start, DateTime end)StartEnd を初期化する
  • メソッド:GetDayCount() を作成し、End - Start の日数を int で返す
    • ヒント:DateTime 同士の引き算は TimeSpan 型になり、.Days プロパティで日数を取り出せる

Main メソッドの処理

  • 次の社員 2 名の入社日と今日 (DateTime.Today) から Period を 1 件ずつ作成
  • それぞれの勤続日数を表示
氏名入社日
山田二郎2001 年 4 月 1 日
佐々木明子2010 年 10 月 1 日

実行結果例(日数は実行日によって変わります):

山田二郎の勤続日数:9183日
佐々木明子の勤続日数:5715日

条件:

  • struct を使う
  • DateTime のプロパティ・引き算・TimeSpan.Days を使う
  • 構造体を別ファイル Period.cs に書く

課題 11-5 null 許容値型で給与・歩合を扱う

Section titled “課題 11-5 null 許容値型で給与・歩合を扱う”

給与と歩合のうち、歩合は未設定(null)になり得るプログラムを作成してください。

Main メソッドの処理

  • 次の 2 名分の社員情報を変数で表現する
氏名salary (int?)commission (decimal?)
山田二郎500000null
佐々木明子8000001200000m
  • 1 名ずつ、給与と歩合を表示する
  • 給与:HasValue で確認し、ある場合のみ表示
  • 歩合:?? を使って null なら 0 を代わりに表示する

実行結果例:

山田二郎の給与:500000円
山田二郎の歩合:0円
佐々木明子の給与:800000円
佐々木明子の歩合:1200000円

条件:

  • int?decimal? を使う
  • HasValue を使う
  • ?? を使う

課題 11-6 DB 接続を想定した Employee クラス

Section titled “課題 11-6 DB 接続を想定した Employee クラス”

第 16 章以降の DB 接続編で使うことを想定し、null 許容値型を含む Employee クラスを作成してください。

クラス仕様

  • ファイル名:Employee.cs
  • プロパティ:
プロパティ名意味
EmployeeIdint社員番号
EmployeeNamestring氏名
HireDateDateTime入社日
Salarydecimal給与
Commissiondecimal?歩合(null あり)
ManagerIdint?上司の社員番号(null あり)
DepartmentIdint部署番号
  • 引数付きコンストラクター(7 つの引数)を作成
  • PrintProfile メソッドを作成し、次の形式で表示する:
    • 歩合や上司番号が null の場合は "未設定" と表示する

Main メソッドの処理

  • 次の社員データで Employee を作成し、PrintProfile で表示する
new Employee(
1001,
"山田二郎",
new DateTime(2001, 4, 1),
500000m,
null, // 歩合なし
null, // 上司なし(社長)
1)

実行結果例:

社員番号:1001
氏名:山田二郎
入社日:2001/04/01
給与:500000円
歩合:未設定
上司ID:未設定
部署ID:1

条件:

  • decimal?int? を使う
  • ?? または if 文で null を扱う
  • 日付は yyyy/MM/dd 形式で表示する

  • プログラムを Visual Studio から実行できる
  • 値型の代入と参照型の代入の違いを確認できている
  • struct で構造体を作成できている(発展)
  • enum で列挙型を作成できている
  • switch 文で列挙型を判定できている
  • null を返すメソッドと null チェックを書けている
  • int?decimal?DateTime? を使えている(発展)
  • HasValueValue を使えている(発展)
  • ?? を使えている(発展)
  • クラス・構造体・列挙型を別ファイルに書けている
  • インデントが整っている
  • 課題を提出した(Git の commitpush、または講師指示があれば Kadai11 フォルダをサーバへコピーし、提出先フォルダに 提出メモ.txt を作成)

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

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

実行例:

Terminal window
git commit -m "Chapter11 タイマー提出: 11-1〜11-2完成、11-3は途中 / null 返却の書き方で詰まった"

Git の詳しい操作は 付録 C、コミットメッセージ形式とサーバコピー提出は本章冒頭「演習課題 > 提出ルール」を参照してください。


この章では、値型と参照型を学習しました。

  • 構造体(struct)は値型、クラス(class)は参照型
  • intboolDateTime などは構造体(=値型)
  • 列挙型(enum)は決まった選択肢を安全に扱うための型
  • 値型は代入時に値そのものがコピーされる
  • 参照型は代入時に参照先(場所)がコピーされ、複数の変数が同じオブジェクトを指すことがある
  • 参照型の new は別のオブジェクトを作る
  • メソッドに渡したときも、値型は値、参照型は参照(場所)がコピーされる
  • null は「参照先がない」状態を表す
  • null の変数を使うと NullReferenceException になる
  • 値型で null を扱いたいときは int?decimal? などの null 許容値型を使う
  • HasValueValue?? で null 許容値型を扱える
  • DB の NULL は C# の null と対応し、int?decimal? などで受け取る

次章では、List<T> と LINQ を学習します。

配列より柔軟に複数のデータを扱える List<T> を使い、さらに LINQ による検索・絞り込み・並び替えを学びます。 後の DB 接続編では、複数件の社員情報を List<Employee> として扱うため、この章と次章の内容が土台になります。