C#の「値型・参照型」と「値渡し・参照渡し」は別の話
C#を学んでいると、「値型」「参照型」「値渡し」「参照渡し」という似た言葉が登場します。ここでよくある疑問が、「参照型をメソッドに渡すと、参照渡しになるのでは?」というものです。
結論から言うと、値型・参照型と、値渡し・参照渡しは別の軸です。
- 値型・参照型:データ型の分類
- 値渡し・参照渡し:メソッド引数の渡し方
まずは、全体像を4象限で確認しましょう。

最初に結論:2つの言葉は見ている対象が違う
| 分類 | 何についての話か | 代表例 |
|---|---|---|
| 値型・参照型 | データ型の分類 | int、bool、struct、class など |
| 値渡し・参照渡し | メソッド引数の渡し方 | 通常の引数、ref、out、in |
C#では、引数に ref、out、in を付けない通常の渡し方は、型が値型でも参照型でも値渡しです。
1. 値型と参照型は「データ型の分類」
値型では値そのものがコピーされる
int、double、bool、char、列挙型、構造体などは値型です。
int a = 10;
int b = a;
b = 20;
Console.WriteLine(a); // 10
Console.WriteLine(b); // 20
b = a では、a が持つ値が b にコピーされます。その後で b を変更しても、a は変わりません。
参照型では参照の値がコピーされる
クラス、配列、文字列、デリゲートなどは参照型です。次の例では、2つの変数が同じ Player インスタンスを参照します。
class Player
{
public int Hp { get; set; }
}
var player1 = new Player { Hp = 100 };
var player2 = player1;
player2.Hp = 50;
Console.WriteLine(player1.Hp); // 50
player2 = player1 でコピーされるのは、Player インスタンスそのものではなく、インスタンスを参照するための値です。そのため、player1 と player2 のどちらからプロパティを変更しても、同じインスタンスに変更が反映されます。
注意:値型は必ずスタック、参照型は必ずヒープ、という単純な整理は正確ではありません。この記事では格納場所ではなく、代入や引数渡しで「何がコピーされるか」に注目します。
2. 通常の引数は型に関係なく「値渡し」
メソッドの仮引数に ref、out、in が付いていなければ、引数は値渡しです。
static void A(int x) // 値型を値渡し
{
}
static void B(Player p) // 参照型を値渡し
{
}
値渡しとは、呼び出し時に変数が持っている値を仮引数へコピーすることです。
intなら、数値がコピーされるPlayerなら、参照の値がコピーされる
3. 参照型を値渡しすると何が起きる?
ここが最も混乱しやすい部分です。参照型を値渡しすると、呼び出し元とメソッド内の変数は別の変数ですが、コピーされた参照の値は同じインスタンスを指します。
インスタンスの内容は変更できる
static void Damage(Player p)
{
p.Hp = 50;
}
var player = new Player { Hp = 100 };
Damage(player);
Console.WriteLine(player.Hp); // 50
p.Hp の変更が呼び出し元からも見えるのは、参照渡しだからではありません。値渡しでコピーされた2つの参照が、同じインスタンスを指しているからです。
仮引数を別のインスタンスへ差し替えても、呼び出し元の変数は変わらない
static void Replace(Player p)
{
p = new Player { Hp = 999 };
}
var player = new Player { Hp = 100 };
Replace(player);
Console.WriteLine(player.Hp); // 100
p は参照の値を受け取った別の変数です。メソッド内で p を差し替えても、呼び出し元の player には影響しません。
4. 値型でも ref を使えば参照渡しにできる
ref を付けると、メソッドはコピーではなく、呼び出し元の変数そのものを参照して操作します。
static void AddTen(ref int x)
{
x += 10;
}
int number = 5;
AddTen(ref number);
Console.WriteLine(number); // 15
int は値型ですが、この引数の渡し方は参照渡しです。つまり、値型だから必ず値渡し、というわけではありません。
5. 参照型を ref で渡すと、呼び出し元の参照変数を差し替えられる
static void Replace(ref Player p)
{
p = new Player { Hp = 999 };
}
var player = new Player { Hp = 100 };
Replace(ref player);
Console.WriteLine(player.Hp); // 999
この例では、メソッド内の p が呼び出し元の player そのものを参照します。そのため、別の Player インスタンスを代入すると、呼び出し元の変数も差し替わります。
6. out と in も「参照による引数渡し」
ref 以外にも、C#には out と in があります。
| キーワード | 主な用途 | 主なルール |
|---|---|---|
ref |
値を受け取り、必要に応じて書き換える | 呼び出し前に初期化が必要 |
out |
メソッドから値を返す | 呼び出し前の初期化は不要。メソッド内で代入が必要 |
in |
読み取り専用の参照として受け取る | 仮引数そのものへ再代入できない。参照先のオブジェクトが不変になるわけではない |
static void CreatePlayer(out Player p)
{
p = new Player { Hp = 100 };
}
CreatePlayer(out Player player);
Console.WriteLine(player.Hp); // 100
out は「出力用の参照渡し」と説明されることが多いものの、ref とは初期化や代入に関するルールが異なります。
4象限で整理
| データ型の分類 | 引数の渡し方 | 例 | コピー・操作の対象 |
|---|---|---|---|
| 値型 | 値渡し | void A(int x) |
値そのものをコピー |
| 値型 | 参照渡し | void B(ref int x) |
呼び出し元の変数を操作 |
| 参照型 | 値渡し | void C(Player p) |
参照の値をコピー |
| 参照型 | 参照渡し | void D(ref Player p) |
呼び出し元の参照変数を操作 |
よくある誤解
誤解1:参照型を渡したら参照渡し
いいえ。void M(Player p) は、参照型を値渡ししています。コピーされる値が参照である、という意味です。
誤解2:メソッド内でオブジェクトが変わったから参照渡し
いいえ。参照型の値渡しでも、呼び出し元と仮引数が同じインスタンスを指していれば、そのインスタンスの変更は双方から見えます。
誤解3:ref は参照型のための機能
いいえ。ref int のように値型にも使えます。ref が変えるのはデータ型の分類ではなく、引数の渡し方です。
まとめ
- 値型・参照型は、データ型の分類
- 値渡し・参照渡しは、メソッド引数の渡し方
- 通常の引数は、値型でも参照型でも値渡し
- 参照型の値渡しでは、参照の値がコピーされる
refを使うと、呼び出し元の変数そのものを操作できるoutとinも参照による引数渡しだが、それぞれルールが異なる
迷ったときは、「型の分類の話か」「引数の渡し方の話か」を先に切り分けてください。この2つを別の軸として考えると、C#の引数渡しをすっきり理解できます。








ディスカッション
コメント一覧
まだ、コメントがありません