クラスが動かない
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2022/1/18 10:46:06
引数の内容を差し替えるような場合は、
ByVal キーワードによる「値渡し」ではなく
ByRef キーワードによる「参照渡し」を用います。
ただし、参照渡しを用いるメソッド設計はあまり行われません。
実際 .NET Framework のライブラリの中でも、ごく限られたケースでしか使われていません。
(「Integer.TryParse メソッド」や「Interlocked.Exchange メソッド」など)
これは、呼び出し側から見た時に、値が書き換えられたことが視覚的に分かりにくい上に、
受け渡しのために必ず変数を用意しなければならないという使いにくさによるものです。
そのため参照渡しを使って引数を直接書き換えるのではなく、
戻り値を用いて結果を返す方が一般的です。
あるいは《値型》ではなく《参照型》を引数として引き渡し、
その参照型のメンバーを編集する方法もあります。これなら ByVal でも動きます。
ByVal キーワードによる「値渡し」ではなく
ByRef キーワードによる「参照渡し」を用います。
Public Sub out(ByRef a As Integer, ByRef b As Integer)
ただし、参照渡しを用いるメソッド設計はあまり行われません。
実際 .NET Framework のライブラリの中でも、ごく限られたケースでしか使われていません。
(「Integer.TryParse メソッド」や「Interlocked.Exchange メソッド」など)
これは、呼び出し側から見た時に、値が書き換えられたことが視覚的に分かりにくい上に、
受け渡しのために必ず変数を用意しなければならないという使いにくさによるものです。
そのため参照渡しを使って引数を直接書き換えるのではなく、
戻り値を用いて結果を返す方が一般的です。
Module Module1
Sub Main()
Dim a As Integer = 2
Dim b As Integer = 5
'この書き方なら、a, b というデータをもとにして、
'左辺の変数が書き換えられるという事が明確になります
Dim result = Example1(a, b)
MsgBox(result.Item1)
MsgBox(result.Item2)
Dim results() = Example2(a, b)
MsgBox(results(0))
MsgBox(results(1))
'この書き方だと、呼び出し元のコードを見ただけでは、
'a, b のデータが書き換わることが分かりにくいです
Out(a, b)
MsgBox(a)
MsgBox(b)
End Sub
'複数の値を返すためにタプルを使う場合
Public Function Example1(a As Integer, b As Integer) As Tuple(Of Integer, Integer)
Return Tuple.Create(CInt(a ^ 2), CInt(b ^ 2))
End Function
'複数の値を返すために配列を使う場合
Public Function Example2(a As Integer, b As Integer) As Integer()
Return {CInt(a ^ 2), CInt(b ^ 2)}
End Function
'引数の内容を差し替えるために ByRef を使う場合
Public Sub Out(ByRef a As Integer, ByRef b As Integer)
a = a ^ 2
b = b ^ 2
End Sub
End Module
あるいは《値型》ではなく《参照型》を引数として引き渡し、
その参照型のメンバーを編集する方法もあります。これなら ByVal でも動きます。
投稿者 こじろー  (社会人)
投稿日時
2022/1/18 11:41:32
ByRefに直すことで、正確に動きました。
VS2019で、web作成の時には、ByValで動いたのですが・・・。
ありがとうございました。
、
VS2019で、web作成の時には、ByValで動いたのですが・・・。
ありがとうございました。
、
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2022/1/18 13:39:32
> VS2019で、web作成の時には、ByValで動いたのですが・・・。
VB6 だろうと VB2022 だろうと、
Web だろうと Console だろうと WinForm だろうと、
すべて同じルールですよ?
ByVal で動いていたというのであれば、その時渡していたのは
Integer などの「構造体」(つまり《値型》)ではなく、
Model などの「クラス」(つまり《参照型》)だったのではないでしょうか。
>> あるいは《値型》ではなく《参照型》を引数として引き渡し、
>> その参照型のメンバーを編集する方法もあります。これなら ByVal でも動きます。
この辺は基本的な事項なので、一応実験コードを載せておきます。
ByVal は「値のコピー」を渡す作業なので、
メソッド側で値型の仮引数を変更しても、呼び出し側の実引数は変化しません。(v1)
メソッド側で値型の仮引数に別の値をセットしても、呼び出し側の変数は差し替わりません。(v2)
ByRef は「元の変数そのもの」の参照が引き渡されるので、
メソッド側で値型の仮引数を変更すると、呼び出し側の実引数も直接変更されます。(v3)
メソッド側で値型の仮引数に別のオブジェクトをセットすると、呼び出し側の変数も差し替わります。(v4)
また、「参照型」はデータの実体(インスタンス)への参照です。
ByRef では「参照情報そのもの」が渡されますが、
ByVal では「参照情報のコピー」が渡されます。
ByVal では、データそのものがコピーされたわけでは無く、
同一データに対する「参照情報」のみがコピーされただけなので、
参照先のデータは完全に同一インスタンスです。
そのため、ByVal でも ByRef でも、メソッド側で参照型オブジェクトのプロパティを書き換えれば、
呼び出し側でも、同一インスタンスのプロパティが書き換わります。(c1、c3)
メソッド側で参照型の仮引数に別のオブジェクトをセットした場合、
ByRef では「元の変数そのもの」の参照情報が差し替わります。(c4)
しかし ByVal では「コピーされた別変数」の参照情報が差し替わるだけなので、
呼び出し元の変数の参照情報は差し替わりません。(c2)
VB6 だろうと VB2022 だろうと、
Web だろうと Console だろうと WinForm だろうと、
すべて同じルールですよ?
ByVal で動いていたというのであれば、その時渡していたのは
Integer などの「構造体」(つまり《値型》)ではなく、
Model などの「クラス」(つまり《参照型》)だったのではないでしょうか。
>> あるいは《値型》ではなく《参照型》を引数として引き渡し、
>> その参照型のメンバーを編集する方法もあります。これなら ByVal でも動きます。
この辺は基本的な事項なので、一応実験コードを載せておきます。
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Me.ListBox1.Items.Clear()
'Point 構造体を「値渡し」「参照渡し」する実験
Dim v1 As New Point(1, 2), v2 As New Point(3, 4)
Dim v3 As New Point(5, 6), v4 As New Point(7, 8)
ByValStruct(v1, v2)
ByRefStruct(v3, v4)
Me.ListBox1.Items.Add("v1 => " & v1.ToString()) '{X=1,Y=2} のまま変化しない
Me.ListBox1.Items.Add("v2 => " & v2.ToString()) '{X=3,Y=4} のまま変化しない
Me.ListBox1.Items.Add("v3 => " & v3.ToString()) '{X=400,Y=6} へと書き換わる
Me.ListBox1.Items.Add("v4 => " & v4.ToString()) '{X=500,Y=600} に差し替わる
'Control クラスを「値渡し」「参照渡し」する実験
Dim c1 As Control = Me.Label1 'c1.Text は "Label1"
Dim c2 As Control = Me.Label2 'c2.Name は "Label2"
Dim c3 As Control = Me.Label3 'c3.Text は "Label3"
Dim c4 As Control = Me.Label4 'c4.Name は "Lable4"
ByValClass(c1, c2)
ByRefClass(c3, c4)
Me.ListBox1.Items.Add("c1 => " & c1.Text) '"ByValClass" に書き換わる
Me.ListBox1.Items.Add("c2 => " & c2.Name) '"Label2" のまま
Me.ListBox1.Items.Add("c3 => " & c3.Text) '"ByRefClass" に書き換わる
Me.ListBox1.Items.Add("c4 => " & c4.Name) '"Button1" に差し替わる
End Sub
'値型の値渡し
Private Sub ByValStruct(ByVal a As Point, ByVal b As Point)
a.X = 100 'メンバーを書き換える
b = New Point(200, 300) '変数の中身そのものを入れ替える
End Sub
'値型の参照渡し
Private Sub ByRefStruct(ByRef a As Point, ByRef b As Point)
a.X = 400 'メンバーを書き換える
b = New Point(500, 600) '変数の中身そのものを入れ替える
End Sub
'参照型の値渡し
Private Sub ByValClass(ByVal a As Control, ByVal b As Control)
a.Text = "ByValClass"
b = Me.Button1 '変数の中身そのものを入れ替える
End Sub
'参照型の参照渡し
Private Sub ByRefClass(ByRef a As Control, ByRef b As Control)
a.Text = "ByRefClass"
b = Me.Button1 '変数の中身そのものを入れ替える
End Sub
End Class
ByVal は「値のコピー」を渡す作業なので、
メソッド側で値型の仮引数を変更しても、呼び出し側の実引数は変化しません。(v1)
メソッド側で値型の仮引数に別の値をセットしても、呼び出し側の変数は差し替わりません。(v2)
ByRef は「元の変数そのもの」の参照が引き渡されるので、
メソッド側で値型の仮引数を変更すると、呼び出し側の実引数も直接変更されます。(v3)
メソッド側で値型の仮引数に別のオブジェクトをセットすると、呼び出し側の変数も差し替わります。(v4)
また、「参照型」はデータの実体(インスタンス)への参照です。
ByRef では「参照情報そのもの」が渡されますが、
ByVal では「参照情報のコピー」が渡されます。
ByVal では、データそのものがコピーされたわけでは無く、
同一データに対する「参照情報」のみがコピーされただけなので、
参照先のデータは完全に同一インスタンスです。
そのため、ByVal でも ByRef でも、メソッド側で参照型オブジェクトのプロパティを書き換えれば、
呼び出し側でも、同一インスタンスのプロパティが書き換わります。(c1、c3)
メソッド側で参照型の仮引数に別のオブジェクトをセットした場合、
ByRef では「元の変数そのもの」の参照情報が差し替わります。(c4)
しかし ByVal では「コピーされた別変数」の参照情報が差し替わるだけなので、
呼び出し元の変数の参照情報は差し替わりません。(c2)
Dim class1 As New class1
Dim a As Integer = 2
Dim b As Integer = 5
class1.out(a, b)
MsgBox(a)
MsgBox(b)
End Sub
Public Class class1
Public Sub out(ByVal a As Integer, ByVal b As Integer)
a =a^2
b =b^2
End Sub
End Class
で、a,bは2と5のままで、a=4やb=25になりません。
何かが足りないのでしょうか、VB2013です。