VB.NETの強制値渡し
メソッドに引数を渡すときに、「値渡し」と「参照渡し」の2つの渡し方があります。
両者の違いについては、プログラム初心者の頃には誰もが一度は頭を悩ませたんじゃないでしょうか。
今回、VB6既存コードをVB.NETに移行するという業務の中で散見したバグが、
結構見つけにくくて危ないなと思ったので、記事にしてみました。
では、さっそく本題へ参りましょう。
(値渡しと参照渡しの違いの説明については割愛します)
“強制値渡し”となってしまう記述
何の変哲もないSetFugaValueメソッドに対し、
以下のように呼び出している箇所がありました。
Module Program
Sub Main(args As String())
Dim hogehoge As String
hogehoge = "HOGEHOGE"
Call SetFugaValue((hogehoge))
'ここでhogehogeメンバーの中身には"FUGAFUGA"が代入されているはず
Console.WriteLine(hogehoge)
End Sub
Private Sub SetFugaValue(ByRef targetValue As String)
targetValue = "FUGAFUGA"
End Sub
End Module
さて、みなさんどう思います?
まぁ題名で気づかれている方がほとんどだと思いますが、、、
そうなんです!このように引数の値に括弧をつけていると、
メソッドの引数がByRef(参照渡し)になっていたとしても、強制的に値渡しになります!!!
よく考えず、なんか引数に括弧ついてるけど気にせずVB.NETへ移行したら
あれ?なんか動き違くない?? でもByRefだから参照渡しのはずでしょ?
どうなってるんだ???・・・とデバッグしてみたら発覚したわけです。
検証してみる
というわけで、一応結果を比べてみましょう。
A:強制値渡し
B:参照渡し
当然、このような違いになりますね。
おそらく既存コードの作成者は、特に意図せず引数に括弧を付けていたんだと思いますが、思わぬところでバグの原因を作ってしまっていたわけですね。
レビューなどの観点にもなりますが、
・意味のない記述はしない
・特別に意味があるなら極力コメント等で明記する
どんな言語だろうと、このあたりを心掛けてコーディングしましょうって事ですかね。
バグの原因になりかねないので、私的にはあまり使用しない方がいいのかなと思っています。(参照渡しで受け取るメソッドに無理やり値渡しで渡したい状況って、そもそも設計が間違ってる気がしますし。。。)
しかし最初に見つけた時はびっくりした。
そんな事できるんかーい!?って笑ってしまいましたよ(笑)
これは目を疑いました。MSDNに書いてあるので仕様なんですね。
https://docs.microsoft.com/ja-jp/dotnet/visual-basic/programming-guide/language-features/procedures/how-to-force-an-argument-to-be-passed-by-value
Visual Studio 2019で強化されているコード支援機能が、どう処理するか試してみました。
“`
Call SetFugaValue((hogehoge))
“`
では、特に修正しろとは表示されませんでした。
さらに(を一つ多く増やして以下のようにすると「不要なかっこを削除しなさい」とコード支援が表示されます。正しく機能しています。
“`
Call SetFugaValue(((hogehoge)))
“`
Resharperはどちらのケースも沈黙を保ちます。
rimeverさん
追加検証ありがとうございます。
VB.NETの特殊な仕様なので、一重括弧”Call SetFugaValue((hogehoge))”の際は
ちゃんと正常系として捉えられているという事でしょうか、、、
最近のコード支援系はホントよくできてますねー。
私的には警告とかにしてくれると嬉しいなーなんて思っちゃいますが。