グローバル変数について

タグの編集
投稿者 allgreen  (社会人) 投稿日時 2020/5/25 13:08:24
ちょっと不思議な状態を見つけたので投稿です
Public Class Form1
    Public 変数1 As Integer
    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Dim f As New Form2
        f.ShowDialog()
        Label1.Text = 変数1 & " " & Form2.変数2 & " " & Form3.変数3
    End Sub
End Class
Public Class Form2
    Public 変数2 As Integer
    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Dim f As New Form3
        Form3.ShowDialog()
        Label1.Text = Form1.変数1 & " " & 変数2 & " " & Form3.変数3
    End Sub
End Class
Public Class Form3
    Public 変数3 As Integer
    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Form1.変数1 = 5
        Form2.変数2 = 4
        変数3 = 3
        Label1.Text = Form1.変数1 & " " & Form2.変数2 & " " & 変数3
    End Sub
End Class
フォームごとに、変数1、変数2、変数3がパブリックで宣言して
フォーム3で変数に数字を代入
フォームを閉じていけば、フォーム2、フォーム1に戻っていく
そのときに、変数1、変数2、変数3を表示
普通に考えれば、3回とも、5 4 3 が表示されると思うのですが
なぜかフォーム2のときの変数2が 0 となります

解決方法としては、グローバル変数を複数箇所で宣言しなければよいのですが
なぜ、このようになるのか不思議です
投稿者 (削除されました)  () 投稿日時 2020/5/25 14:18:51
(削除されました)
投稿者 葉月  (社会人) 投稿日時 2020/5/25 14:24:51
1.Dim f As New Form2を宣言し、f.ShowDialog()
2.Form2.ShowDialog()

上記二つの呼び出しは同じ結果になるように見えますが、実は違います。
(検証の目的が見えなかったので解決策だけ書いておきます)
想定した動きにするだけなら全フォームを2.に書き換えてください。
Form2からForm3を呼び出すなら――
Form3.ShowDialog()を使います。

それから同じようにForm4も追加してはどうでしょう。
変数2だけが0になる訳じゃないとわかります。
投稿者 shu  (社会人) 投稿日時 2020/5/25 16:12:03
関連する変数は以下5つありそれぞれ異なるものとなっています。
①Form1.変数1
②Form1のButton1_Clickのf.変数2
③Form2.変数2
④Form3.変数3
⑤Form1のButton1_ClickのfのButton1_Clickのf.変数3



設定処理について
Form3のButton1_Clickでの変数設定
 Form1.変数1 = 5   ①に設定
  Form2.変数2 = 4       ③に設定
  変数3 = 3                ④に設定



参照処理について
Form1のButton1_Clickでの
 Label1.Text = 変数1 & " " & Form2.変数2 & " " & Form3.変数3  ①、③、④

Form2(Form1のButton1_Clickで作成されたf)のButton1_Clickでの
  Label1.Text = Form1.変数1 & " " & 変数2 & " " & Form3.変数3     ①、②、④ ※②の値なので0となる。

Form3のButton1_Clickでの
  Label1.Text = Form1.変数1 & " " & Form2.変数2 & " " & 変数3     ①、③、④
投稿者 allgreen  (社会人) 投稿日時 2020/5/25 16:15:11
確かに、書かれているようにすると、結果が違うのがわかりました
次の疑問としては
他のフォームを呼び出すのは、すべて
form2.showdialog でよくなるのでは?
インターネットを見ると、ほとんどの場合ほかのフォームを呼び出すのは
dim f as new form2
f.showdailog
となってます

未だに、VB6.0の感覚が抜けていなくて
load form2
form2.show vbmodal
の方がわかりやすい
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/5/25 17:03:42
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
  Dim f As New Form2()
  f.Show(Me)
End Sub


上記の場合、ボタンを複数回押すと、Form2 が複数生成されますね。
その場合、MsgBox(Form2.Left) とした場合、どのフォームの座標が表示されるのか…という話になります。

なので、暗黙のフォームインスタンスを使ったコーディング手法と、
明示的なフォームインスタンスの管理手法を混在させることは望ましくありません。
どちらか一方に統一すべきかと思います。これは VB6 であっても同じことです。
投稿者 shu  (社会人) 投稿日時 2020/5/25 17:03:50
>未だに、VB6.0の感覚が抜けていなくて
>load form2
>form2.show vbmodal
>の方がわかりやすい

こういう方の為にデフォルトのインスタンスというものが使用出来るようになっています。
Formより派生させたクラスの場合、クラス名でのインスタンスを自動で作成させることができます。

他のオブジェクト指向言語と合わせるのであれば
Dim f as new form2
f.showdailog()
と記述された方が良いでしょう。




投稿者 (削除されました)  () 投稿日時 2020/5/25 17:15:03
(削除されました)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/5/25 17:17:30
付け加えるとすれば、
> Dim f As New Form2
> f.ShowDialog()
というコードも適切ではありません。正しくは下記のように書くべきです。
Using f As New Form2()
    f.ShowDialog()
End Using



Show の場合は、子フォームが閉じられた時に、子フォームのインスタンスも Dispose されます。

しかし ShowDialog の場合、子フォームが Close メソッドや呼ばれたり、
右上閉じるボタンが押されたとしても、単に非表示になるだけで
インスタンスは残り続けます。
そのため、インスタンスを生成した側で、明示的に Dispose しなければならないのです。


https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.form.showdialog?view=netframework-4.8
>
> 非モーダルフォームとは異なり、Close メソッドは、ユーザーがダイアログボックスの
> [フォームを閉じる] ボタンをクリックしたとき、または DialogResult プロパティの値を
> 設定したときに .NET Framework によって呼び出されません。 代わりに、フォームは
> 非表示になり、ダイアログボックスの新しいインスタンスを作成せずに再び表示できます。
>
> ダイアログボックスとして表示されるフォームは、Closed ではなく非表示になるため、
> アプリケーションでフォームが不要になったときに、フォームの Dispose メソッドを
> 呼び出す必要があります。
>

VB6 の場合も、Unload しただけではそのフォームのインスタンスは残り続けており、
フィールド変数に引き続きアクセスできましたよね?
内部事情的は少々異なっていますが、それと似たような話だと思ってください。

http://www.gizcollabo.jp/vbtomo/log/archive/vbqanda_30162_0.html
https://www.nda.co.jp/memo/event_vb6.html
投稿者 葉月  (社会人) 投稿日時 2020/5/26 05:31:07
>他のフォームを呼び出すのは~となってます
私もUsingを使い、後者の呼び出しで書いています。
Form間でやり取りが必要ならインターフェースを作り、プロパティーを使います。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/5/26 09:13:45
> 他のフォームを呼び出すのは、すべて
> form2.showdialog でよくなるのでは?

モーダルにすると、子画面を表示した後で呼び出し元を最小化するといった事ができず、
ユーザー操作の妨げになります。「他の画面を操作されては困る」といった場合に
モーダルにするのは良いですが、それ以外の場合は極力モードレスにすることが望ましいです。
https://www.weblio.jp/content/%E3%83%A2%E3%83%BC%E3%83%89%E3%83%AC%E3%82%B9%E3%83%80%E3%82%A4%E3%82%A2%E3%83%AD%E3%82%B0


また、Form2.ShowDialog に頼った実装だと、引数付きコンストラクタを利用できなくなるので、
初期化処理などが制限されてしまいがちです。そのため、自分はモーダルなら

'呼び出し側の処理 
Using f As New Form2(引数1, 引数2)
  If f.ShowDialog() = DialogResult.Cancel Then
        Return
    End If
    Me.Label1.Text = f.ResultText   '独自プロパティで結果を返す 
End Using


というパターンもしくは

Dim selectedFiles As String() = Form2.ShowFilesDialog(targetFolder)    '自作の Shared メソッド 
If selectedFiles.Length = 0 Then
    Return
End If


な形式にすることがあります。
後者の場合、外部から New Form2() できないよう、コンストラクタを Private にします。