導入
「値型と参照型で挙動が違う」のはなぜか——その答えはメモリの使われ方にあります。値はスタック、オブジェクトの実体はヒープに置かれ、不要になった実体は**GC(ガベージコレクタ)**が自動で回収します。C/C++と違い手動解放は不要ですが、ファイルや接続などの外部資源だけはIDisposableで明示的に片付けます。この全体像を掴むと、参照の共有やメモリ効率の判断ができるようになります。
図解
flowchart TB
subgraph Stack["スタック(高速・自動)"]
V["値型 int / 変数そのもの"]
end
subgraph Heap["ヒープ(GCが管理)"]
O["参照型の実体<br/>class・配列・List"]
end
V -.->|参照型の変数は実体を指す| O
GC["GC が不要な実体を自動回収"] --> Heap
サンプル
// 参照型の代入は「同じ実体」を指す(値のコピーではない)
var arr = new[] { 1, 2, 3 };
var b = arr; // 参照のコピー(同じ配列を指す)
b[0] = 99;
Console.WriteLine(arr[0]); // 99 ← 同じ実体を共有しているため
// 使い終わった実体は GC が自動回収する。手動 free は不要。
// ただし「ファイル・DB接続」など外部資源は IDisposable で明示的に解放する
using (var res = new Resource())
{
res.Use();
} // ブロックを抜けると自動的に Dispose() が呼ばれる
Console.WriteLine("解放済み");
class Resource : IDisposable
{
public void Use() => Console.WriteLine("使用中");
public void Dispose() => Console.WriteLine("後片付け");
}
- 値型(int/struct)は値そのもの、参照型(class/配列/List)は「実体を指す矢印」
- 参照型の代入・引数渡しは同じ実体を共有する(struct/classの違いの根拠)
- 不要になった実体はGCが自動で回収するのでメモリ解放は基本気にしなくてよい
- ファイル・接続など外部資源は
IDisposable+usingで確実に解放する(usingの背景)
演習
var list1 = new List<int> { 1, 2, 3 };
var list2 = list1; // 参照のコピー(同じ実体)
list2.Add(4);
// TODO: list1 の要素数を出力してください(同じ実体なので 4)
___
- 期待される出力:
4
ヒント1を見る
list2はlist1と同じ実体なので、Addは両方に反映されます
ヒント2を見る
Console.WriteLine(list1.Count);
まとめ
- 値型はスタック、参照型の実体はヒープに置かれる
- 参照型の代入は実体の共有(値のコピーではない)
- 不要な実体はGCが自動回収、外部資源は
IDisposable+usingで解放
次章: 良い設計の指針「SOLID原則」へ進みます。