トップ回答者
SDIとMDIにおける Public変数の違い

質問
-
VB2012にて、SDIだと正常に動作しますが、MDIにするとPublic変数が更新出来ません。
Public変数Aがあるものとして、20が代入されているのとします。
子フォームで、21に更新し、その後子フォームより、ダイアログフォーム(ShowDialg()にて)を表示し、ダイアログボックスが消えた時点でAの値が20に戻ります。
ダイアログフォームを呼び出した、子フォームのForm_Activeで書き換わっているのではないかと疑って調べましたが、それはありませんでした。
又、「A=20」の箇所全てにブレークポイントを設定し、そこで書き換えされていないか確認しましたが、それもありません。
そもそも、SDIで動作すれば問題ないのですから、SDIとMDIにおけるPublic変数の違いとしか思えません。
ちなみに、Friend変数にしても同じでした。
原因が変わらず途方に暮れています。
原因か又は対策をご存知の方がおいでになりましたら、ご教示お願いできれば幸いです。
回答
-
Public A As Integer
で宣言しています。
その変数を、他の Function や Sub の引数に渡しているところは無いでしょうか。
極端に言えば、こういった状況です。
Private Sub Sample(ByRef x As Integer) x = A / 2 End Sub Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click A = 40 Sample(A) End Sub
この場合、変数 A への代入は「A = 40」 としか行われていませんが、最終的には、変数 A の値が 20 に書き換わります。
それゆえ、『「A=20」の箇所全てにブレークポイントを設定』するだけでは、調査としては不十分だと思います。上記 Sample メソッドで「x = 30」と書かれていた場合、それは「A = 30」に相当する動作になるわけですから。上記は極端な例ですし、それだけで今回の現象の説明が付くわけではありませんが、SDI / MDI の違いで状況が変わるという事から、「Public A As Integer」や「フォームのインスタンス」の管理が、きちんと意識付けされていない可能性があり、それが今回の問題を引き起こしているのでは無いかと予想しています。
たとえば…プログラム内でフォームの表示と非表示が行われる箇所は無いでしょうか。(ShowDialog を行っているのですから、少なくとも一箇所はあるはずです)
少し確認しておきたいのですが、複数のフォームを扱うにあたり、以下の点は把握されているでしょうか。
- モードレスで呼び出したフォームは、右上×ボタンや Close メソッドで閉じられると、破棄される。
- モーダルで呼び出したフォーム(ShowDialog 呼び出し)は、右上×ボタンや Close メソッドで閉じられると「非表示」になるが破棄はされない。
後者(ShowDialog)については、閉じられても単に『非表示』になるだけなので、一度閉じたフォームを再利用し、再度表示しなおすことができます。しかし逆に言えば、閉じただけではメモリ上から即時回収されないため、呼び出し側で明示的に Dispose を呼び出す(あるいは Using の利用)が求められます。
一方で前者(Show メソッド、あるいは .Visible = True での表示)の場合、閉じられると直ちに Dispose されるため、その後で、同じインスタンスを Show しなおすことができない仕様です。無理に再表示しようとしても、例外(ObjectDisposedException)が発生することになります。(意図的に非表示にしたい場合には、Hide メソッドや .Visible = False を用いることができます)
ところが、自身でフォームを New して管理するのではなく、Form の既定のインスタンス (Form の暗黙的なインスタンス化)に頼っていた場合、前者の動作が曖昧になりがちです。
というのも、Show されたフォームを閉じた後で再度 Show したとしても、ObjectDisposedException が発生することも無く、そのまま何事も無かったかのように表示されてしまうためです。
既定のインスタンスを用いた場合、破棄されたフォームに再アクセスされた時点で、フォームが再生成される仕様です。再生成されたフォームの変数やプロパティは、起動直後の状態に戻ることになります。
ですから、こうした動作の違いについて十分理解しないままコーディングを進めていると、間接的に、Module の変数を意図しないタイミングで書き換えてしまっているというコーディングミスを生じてしまっている可能性があるように思います。実際のコード内容が提示されていないので、実際にそうなのかどうかは判断できませんが…。
- 回答としてマーク turutakun 2015年8月10日 7:05
すべての返信
-
SDI か MDI によって変数の扱いが変わると言うことは考えられません。
SDI と MDI で作り方を変えているところがあるはずでしょうから、そこの問題の可能性が高いです。
1つ考えられるのは、「A」がインスタンス変数であり、複数のインスタンスが存在すると言うこと。
「A が 21 に変わること」と「A が 20 に戻ること」の観測方法が違うのであれば、正しく事象を捉えられていない可能性が出てきますのでご注意ください。現時点では、サンプルコードが提示されていないので、これ以上の推測は難しいです。
- 編集済み AzuleanMVP, Moderator 2015年8月2日 7:26
- 回答の候補に設定 星 睦美 2015年8月3日 7:29
-
実際にコードを見ていないので何とも言えませんが、VBだと既定のインスタンス(暗黙のインスタンスとも言われる)の問題かもしれませんね。
(参考)
Formを呼び出す場合にNewは絶対に必要なのでしょうか
https://social.msdn.microsoft.com/Forums/ja-JP/55781635-aaee-4f5b-85bb-29f3b4ce7c03/formnew★良い回答には回答済みマークを付けよう! MVP - .NET http://d.hatena.ne.jp/trapemiya/
-
turutakun さま よろしく。
A=20 の箇所で が気になります。
A=20 は Me.A=20 ですね。子のクラスの中で
A を宣言していたりしませんか?。親の Form のクラスを Form1 と仮定し ここで A を宣言したとすれば、子のクラスの中で、
A=21 を Form1.A=21 と直すと如何でしょうか?。- 編集済み ShiroYuki_Mot 2015年8月3日 7:53 A の宣言場所の仮定を挿入
-
あり得ないことが起きており、ご質問の本質はどうデバッグして問題点を見つけ出すかということのように思えます。よって、回答が出にくい状況のように思えますので、Azuleanさんも書かれていますが、問題が再現するミニマムのコードを示していただければと思います。
しかし、その前に、可能であれば以下を試してみて下さい。
1.Aを一時的にプロパティに変更し、セッターにブレークポイントを設定する。
2.ブレークポイントで止まったら「呼び出し履歴」を確認し、どの行でAを変更しているのかを見る。
以上を試してみても不明な場合には、ミニマムのコード等、できるだけ情報を出していただけると、解決に近づくと思われます。
★良い回答には回答済みマークを付けよう! MVP - .NET http://d.hatena.ne.jp/trapemiya/
-
Public A As Integer
で宣言しています。
その変数を、他の Function や Sub の引数に渡しているところは無いでしょうか。
極端に言えば、こういった状況です。
Private Sub Sample(ByRef x As Integer) x = A / 2 End Sub Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click A = 40 Sample(A) End Sub
この場合、変数 A への代入は「A = 40」 としか行われていませんが、最終的には、変数 A の値が 20 に書き換わります。
それゆえ、『「A=20」の箇所全てにブレークポイントを設定』するだけでは、調査としては不十分だと思います。上記 Sample メソッドで「x = 30」と書かれていた場合、それは「A = 30」に相当する動作になるわけですから。上記は極端な例ですし、それだけで今回の現象の説明が付くわけではありませんが、SDI / MDI の違いで状況が変わるという事から、「Public A As Integer」や「フォームのインスタンス」の管理が、きちんと意識付けされていない可能性があり、それが今回の問題を引き起こしているのでは無いかと予想しています。
たとえば…プログラム内でフォームの表示と非表示が行われる箇所は無いでしょうか。(ShowDialog を行っているのですから、少なくとも一箇所はあるはずです)
少し確認しておきたいのですが、複数のフォームを扱うにあたり、以下の点は把握されているでしょうか。
- モードレスで呼び出したフォームは、右上×ボタンや Close メソッドで閉じられると、破棄される。
- モーダルで呼び出したフォーム(ShowDialog 呼び出し)は、右上×ボタンや Close メソッドで閉じられると「非表示」になるが破棄はされない。
後者(ShowDialog)については、閉じられても単に『非表示』になるだけなので、一度閉じたフォームを再利用し、再度表示しなおすことができます。しかし逆に言えば、閉じただけではメモリ上から即時回収されないため、呼び出し側で明示的に Dispose を呼び出す(あるいは Using の利用)が求められます。
一方で前者(Show メソッド、あるいは .Visible = True での表示)の場合、閉じられると直ちに Dispose されるため、その後で、同じインスタンスを Show しなおすことができない仕様です。無理に再表示しようとしても、例外(ObjectDisposedException)が発生することになります。(意図的に非表示にしたい場合には、Hide メソッドや .Visible = False を用いることができます)
ところが、自身でフォームを New して管理するのではなく、Form の既定のインスタンス (Form の暗黙的なインスタンス化)に頼っていた場合、前者の動作が曖昧になりがちです。
というのも、Show されたフォームを閉じた後で再度 Show したとしても、ObjectDisposedException が発生することも無く、そのまま何事も無かったかのように表示されてしまうためです。
既定のインスタンスを用いた場合、破棄されたフォームに再アクセスされた時点で、フォームが再生成される仕様です。再生成されたフォームの変数やプロパティは、起動直後の状態に戻ることになります。
ですから、こうした動作の違いについて十分理解しないままコーディングを進めていると、間接的に、Module の変数を意図しないタイミングで書き換えてしまっているというコーディングミスを生じてしまっている可能性があるように思います。実際のコード内容が提示されていないので、実際にそうなのかどうかは判断できませんが…。
- 回答としてマーク turutakun 2015年8月10日 7:05
-
ご回答ありがとうございます。
ご指摘の、引数に渡してるということは、把握している範囲ではありません。
また、ご指摘の、フォームのインスタンスの管理の意識付けについては、仰せの通りかもしれません。自信はありません。
モードレス、モーダルの動作は一応把握しているつもりです。それなりにコードを書いてるつもりです。
モーダルの場合、制御が呼び出し元に戻った時点で必ずDisposeで開放させていはずです。(全体の中では、抜けはあるかもしれませんが、問題個所に関しては間違いないと思います)
まだまだスキル不足で、何か間違いを犯しいる可能性はあります。
ご教示内容を踏まえて再度見直してみます。
ありがとうございます。
-
ようやく原因が判りました。
一番最初に疑った親フォームのForm_Activeが問題でした。
問題確認直後、ダイアログ表示後に、Form_Activeでブレークポイント設定し、捉えようと思ったのですが、なぜかブレークポイントではトラップできなかったので、関係ないとずっと思い込んでいました。
皆様の回答を頂いてからも、いろいろと試したのですが判らず途方に暮れて、魔界の仮面弁士様のご指摘から、再度最初から疑って、何度も条件を変えて1ステップづつ地道にトレースした結果、予想外のところでForm_Activeを実行していました。
初心者レベルの(実際初心者に近いですが)ミスで皆さんにご迷惑をおかけしました。
ご回答いただいた皆さんに、改めて御礼申し上げます。
ありがとうございました。