はじめに
・「参照型渡すときにref
パラメータ修飾子つける意味あんの?」
・「参照型でも値(プリミティブ)型でも値渡し」→「??」
C#のパラメータ修飾子について学んでいるときに、
上記の疑問がわいたので調べた内容をまとめた記事です。
これ↓って参照渡しじゃないよ。
void Test() { List<Hoge> a = new List<Hoge>(); Val(a); Debug.Log("count = " + a.Count); } private void Val(List<Hoge> b) { b.Add(new Hoge()); }
▼実行結果
count = 1
上記のソースで「countが増えてる=参照渡しだ」と判断していたのですが、
参照渡しではなく値渡しです。
参照型の値渡し
上記のソースでは、
・参照型変数a
はListの参照先を保持した変数です。
・参照型変数a
を値渡しします。
値渡しをするということは、参照型変数a
の値=Listの参照 をコピーします。
コピーされた変数が参照型変数b
です。
・Val()
の中で、参照型変数b
の値=Listの参照 を元にListにAddします。Listのカウント数は1になりました。
・Test()
の中で、参照型変数a
の値=Listの参照 を元にListのカウント数をデバッグ出力します。
ということが行われています。
参照型変数a
もb
が持つ参照の参照先のListは同じなのですが、それぞれで参照をもっているということです。
以下のようなイメージです。
参照型の「参照渡し」
一方、ref
を用いて「参照渡し」した場合は、
同じ参照を参照型変数a
もb
も持つことになります。
以下のようなイメージです。
結果が異なるパターン
先程のソースでは実行結果が同じだったので、実行結果が異なる例を挙げます。
void Test() { List<Hoge> a = new List<Hoge>(); Ref(ref a); //Val(a); RefかValのどちらか呼ぶ Debug.Log("count = " + a.Count); } private void Val(List<Hoge> b) { b = new List<Hoge>(); b.Add(new Hoge()); } private void Ref(ref List<Hoge> b) { b = new List<Hoge>(); b.Add(new Hoge()); }
▼Ref()
(参照渡し)を呼んだときの実行結果
count = 1
▼Val()
(値渡し)を呼んだときの実行結果
count = 0
このように結果が異なります。
先程、値渡しをした場合はコピーされ、
変数a
,b
それぞれで参照をもっていると書きました。
‘Val()‘ の中で変数b
が持っている参照の参照先が新しくnewされたListに変わります。
変数a
の参照先は変わりません。
Test()
では変数a
の参照先をデバッグ出力しているので、カウント数は0のままになります。
以下のようなイメージです。
次に参照渡しの場合です。
参照渡しの場合は変数a
とb
がもっている参照は同一です。
‘Val()‘ の中で変数b
が持っている参照の参照先が新しくnewされたListに変わります。
変数a
と変数b
が持っている参照は同一なので、変数a
の参照先も新しくnewされたListに変わります。
以下のようなイメージです。
最後に
このように、参照型の値渡しと参照渡しとでは動きが異なります。
最初に疑問に思ったことの回答・・・
「参照型渡すときにref
パラメータ修飾子つける意味あんの?」
→ある。無駄なコピーはなくせるのでref
はつけた方がいい。
だけど無駄なコピーなくすってメリットぐらいしか思いつかん。。(※)
ほかにメリットある??
(※)今回例であげたような、
呼び出し先のメソッドでnew されるのが前提のソースの場合は、
呼び出し元でnewせずパラメータ修飾子もref
じゃなくてout
でもらうべきだと思うからです。
参考
参考にさせてもらいました、あざしたー★!