VB.Net
VB6.0

メソッドの引数における値渡しと参照渡しについて


はじめに

メソッド(関数)の引数には2通りの渡し方があります(言語によっては1通りしかない場合もあります)。1つが値渡しで、もう1つが参照渡しです。プログラミングに初めて触れてから2か月&&実務1週間のときにこのことで少しつまづいたので、その辛さを思い出すために書き残しておきます。

解説のためのコードですが、参照渡しを使えるC#は全然書けないので、需要はありませんがVBで書きます。VBわかんねえよ!って人も、VBはノリで読める言語なので問題ないと思います。

(余談ですが、Basicというプログラミング学習用言語がVBのもとになっているみたいです。なので、VBはゆーたらScratchみたいなもんです)


値渡しとは

値渡しとは、メソッドの引数としてオブジェクトの参照を渡さずに値のコピーだけ渡すことです。言葉だとわかりにくすぎるので具体例を出します。


sampleVal.vb

Public Sub sampleVal(ByVal i As Integer, ByVal j As Integer)

i = i * 10
j = j * 100
End Sub

Dim a As Integer = 1

Dim b As Integer = 2
sampleVal(a,b)
Console.WriteLine(a) '1と出力される
Console.WriteLine(b) '2と出力される

(上の「ByVal」というのが「値渡しで渡すよ」というメッセージです。VB.Netでは省略すると値渡しで渡されます)

このように、引数としてメソッドに渡された変数に入っている値は、渡された先のメソッドの中でどんな処理が行われようとも変わりません(当たり前やんけって思う気持ちはとてもわかります)。ただし、参照の値渡しという、値渡しについてのややこしい話もあります。それについては後述します。

メソッドの引数として渡す場合は多分だいたい値渡しです。そもそも値渡ししかない言語もあります(Javaとか、Rubyとか、そのほかいっぱい)。


参照渡しとは

参照渡しとは、メソッドの引数としてオブジェクトの参照を渡すことです。言葉だとわかりにくすぎるので具体例を出します。


sampleRef.vb

Public Sub sampleRef(ByRef i As Integer, ByRef j As Integer)

i = i * 10
j = j * 100
End Sub

Dim a As Integer = 1

Dim b As Integer = 2
sampleRef(a,b)
Console.WriteLine(a) '10と出力される
Console.WriteLine(b) '200と出力される

(上の「ByRef」というのが「参照渡しで渡すよ」というメッセージです)

このように、参照渡しだと引数としてメソッドに渡された変数に入っている値は、渡された先のメソッドで処理が行われると、処理が行われた状態で返ってきてしまいます。(こういう、引数で渡した変数が変わってしまうようなメソッドを破壊的メソッドと呼んだりします)

これを用いると戻り値的なものを大量に戻すことも可能です()


実は値渡しでも参照渡しっぽいことになる

実は値渡しで引数を渡したとしても、参照型の変数であれば参照渡しっぽくなります。これも具体例を出します。


sampleClass.vb

Public Class Sample

Public Property _number As Integer
End Class


sampleRefPpoi.vb

Public Sub sampleRefPpoi(ByVal sample As Sample)

sample.Number = sample.Number * 10
End Sub

Dim sample As New Sample()

sample.Number = 10
sampleRefPpoi(sample)
Console.WriteLine(sample.Number) '100と出力される

何が起きているかについて説明します。

値渡しでは変数の値がコピーされて呼び出したメソッドに渡されるのですが、参照型を引数とする場合は、コピーされる値が「参照」なのです。

参照型の変数では、オブジェクトがそのまま変数の中に入っているわけではなく、「オブジェクトの居場所」が入っているだけなので、メソッドに渡される値も「オブジェクトの居場所」になります。

つまり、引数の受け手であるメソッドでは、「オブジェクトの居場所」を渡されて、その居場所にあるオブジェクトに対して直接読み書きできるので、以上のような破壊的なメソッドとなってしまいます。

このような値渡しを「参照の値渡し」と呼んだりします。

ただし、「参照の値渡し」は、あくまで「参照渡しっぽい」だけであって実際には参照渡しとは異なります。これについてはまた別途記事を書きたいと思います。


あれ、Stringって参照型じゃね?

ここで、String型の変数を渡したときは別にならなくね?あれ?ってなった方もいらっしゃるかもしれません。

実はString型は参照型の中でもちょっと特殊みたいで、値渡しで渡しても参照先のオブジェクトは変わらないようです。String型はchar型の配列で、String型の変数に何か操作を加えると参照する居場所が変わるようになっているからみたいです。


最後に余談

冒頭で「プログラミングに初めて触れて2ヶ月&&実務1週間のときにこのことで少しつまづいたのでその辛さを思い出すために書き残しておきます」と言った理由を話したいと思います。

VB.Netではメソッドの引数に何も指定しなかった場合、値渡し(ByVal)で渡されることになっています。一方、VB6.0では参照渡し(ByRef)で渡されることになっています。(なんでやねん)

僕はJavaでプログラミング人生を始めたため、参照渡しという(別に必要ない)仕様を知りませんでした。それも相まって、VBについて何も知らないままコーディングするときに「なんで文字コード変換するためのメソッドで戻り値がBoolean型なん...??」ってなりました。

(「このメソッドで文字列の文字コードをSJISからUTF-8に変えるよ!」って言われたら、ふつうは引数が変換前の文字列で、戻り値が変換後の文字列だと思いますよね?)

何が言いたいかというと、VB6.0はそろそろ絶滅してくれということです。

終わり。