none
フォーム1の配列のデータをフォーム2に渡したい RRS feed

  • 質問

  • フォーム1で用いている配列のデータ<x1()>をフォーム2に渡したいのですが実行すると、「System.NullReferenceException
    オブジェクト参照がオブジェクト インスタンスに設定されていません。」といったエラーが出ます。何が問題なのでしょうか。ソースは以下の通りです。

    <フォーム1>

    Public Class Form1

        Private Shared _form1Instance As Form1

        Public Shared Property Form1Instance() As Form1
            Get
                Return _form1Instance
            End Get
            Set(value As Form1)
                _form1Instance = value
            End Set
        End Property

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            x1()を用いる計算プログラム

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Private Shared x1 As Double()

        Public Property Gurahux1 As Double()
            Get
                Return x1
            End Get
            Set(ByVal value() As Double)
                x1 = value
            End Set
        End Property


        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
            Hide()
            Form2.ShowDialog()
            Show()
        End Sub

    End Class

    <フォーム2>

    Public Class Form2
        Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load

            F2TB1.Text = Form1.Form1Instance.Gurahux1(0)
        End Sub

    End Class

    (今回はテキストボックスに配列の最初の数値だけ入れるという動きにしています)

    お忙しいとは思いますがなにか助言等いただければ幸いです。

    2019年1月19日 3:27

回答

  • おんやさい252 さま よろしく。

    単に、Form1 に Button2、Form2 に TextBox F2TB1 を置いただけで、
    Form1 の以下のコードでは如何ですか?。

    Public Class Form1
        Friend x1s As Double() = {1.0, 2.0, 3.0}

        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            x1s(0) = 1.5
        End Sub

        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            'Dim x1s As Double() = {1.0, 2.0, 3.0}
            Dim f2 As New Form2
            f2.F2TB1.Text = x1s(0)
            f2.ShowDialog()
        End Sub
    End Class

    一方通行で渡すだけなら、Form2 に記述は不要です。


    • 編集済み ShiroYuki_Mot 2019年1月19日 14:51 コード変更 値はきっと何処かで処理の筈
    • 回答としてマーク おんやさい252 2019年1月20日 12:08
    2019年1月19日 14:42

すべての返信

  • Form1 内のコードで Form2 を初期化した後 Form2 を表示しているのだと思いますが、であれば、Form2 のコンストラクタに配列Double() を引数に持つものを追加し、Form1 内で Form2 を初期化する際に Form2 のコンストラクタの引数に x1 を渡すようにしてはいかがですか? 

    質問者さんが書いているコードの 10 倍ぐらい簡単&スマートになると思います。
    2019年1月19日 3:39
  • プログラムのどこでNullReferenceExceptionが発生しているかわかりますか? また、 NullReferenceExceptionの意味はご理解されてますか?

    ざっとプログラムを拝見したところ配列X1をどう渡すか、ということとは別の問題としてNullReferenceExceptionが発生しているんじゃないかと疑います・・・。Form2.ShowDialog()のところなのですが・・・。

    Instanceの文字があるので「インスタンス」の意味はご理解されているかも? と拝察しますが・・・、プログラム実行時にクラスForm1、クラスForm2、クラスDouble()つまりDouble配列のX1、いずれもインスタンスを作るためのNewキーワードがプログラムに見当たらず、NullReferenceExceptionが発生する可能性がいろいろ考えられます。

    ただ、今のVisual BasicはクラスFormから派生させた場合、クラスを宣言するだけでインスタンスが生成される仕組みがあるらしいので、細かいところは質問者さんのプログラミングされている環境の設定次第ではあるのですが。

    • 編集済み 外池 2019年1月19日 9:44
    2019年1月19日 9:29
  • 返信ありがとうございます!

    フォーム2に以下のソースを追加したところ、フォーム1の「Form2.ShowDialog()」の部分に「非共有メンバーを参照するには、オブジェクト参照が必要です」というエラーが生じてしまいました。

    Sub New(x1() As Double)
            F2TB1.Text = x1(0).ToString
        End Sub

    2019年1月19日 10:35
  • 外池さん返信ありがとうございます!

    エラーはフォーム2の「F2TB1.Text = Form1.Form1Instance.Gurahux1(0)」で出てきました。NullReferenceExceptionはネットで軽く調べただけなのですが、戻り値がnullの場合にこのようなエラーが出るという認識です。

    F2TB1.Text = Form1.Form1Instance.Gurahux1(0)の部分を消し、フォーム2を表示するだけなら問題なくできました。

    率直に申しますと、propertyや二つ目のフォームの表示なども含めネットで調べた断片的な情報をもとにプログラムを組んでいくのが主なので根本的なことはあまり理解していないというのが現状です、、、、

    2019年1月19日 10:55
  • Visual Basicは、簡略化したプログラムの書き方が出来てしまって、遭遇されたNullReferenceExceptionの意味をご説明することがかえって難しくなってしまうことがあって・・・、上手く伝わるかどうか不安ですが、以下参考にして下さい。

    ーーーーーー面倒なら読み飛ばして下さい(始まり)ーーーーーー

    Public Class Form1から始まってEnd Classまでのプログラム、あとPublic Class Form2から始まってEnd Classまでのプログラム、それぞれはForm1というクラスとForm2というクラスを定義しています。「クラス」とは部品の型番のようなもので「定義している」というのは、設計図を描いた、というような意味です。

    Form1もForm2も画面上に表示できる部品で、表示させてみるとウィンドウの姿をしている、ということはご存知だと思います。Form1という型番のウィンドウを同時にいくつも表示させることができますし、Form2という型番のウィンドウを同時にいくつも表示させることができます。ひとつひとつのウィンドウがインスタンスと呼ばれるもので、「インスタンス」はある型番の部品の実物という意味になります。

    Form1という型番の部品つまりウィンドウを複数表示させて、それぞれ何か操作したいなら、あの実物とこの実物とあっちの実物・・・と区別して操作しないといけないわけですが、「あの」「この」「あっちの」という区別を「参照」と言います。そういう区別を記録・保持するために「変数」を用意することになります。型番つまりクラスの名称と変数の名称は本来別物なんですが・・・、Visual Basicではクラスの名称と、インスタンスへの参照を記録・保持する変数の名称を、ゴッチャにして簡略化して記述できてしまう特徴があって・・・、これって、Form1やForm2のようにFormから派生したクラスの場合だけだったかなぁ・・・。

    ーーーーーー面倒なら読み飛ばして下さい(終わり)ーーーーーー

    で・・・、

    Private Shared x1 As Double()とありますが、これは、Double配列型というクラス(部品)のインスタンス(実物)を作ったことになっていない、ということを知っておいて下さい。配列の実物は出来ていないんです。また、x1という変数が配列そのものになるわけでもないんです。x1はゆくゆく作られるDouble配列型のインスタンス(実物)への参照を記録・保持するものであって、この一行を書いただけでは何も参照していません。これがNullReferenceExceptionが発生した理由です。参照(Reference)が何もされていない(Null)という意味です。

    Sub New(x1() As Double)/F2TB1.Text = x1(0).ToString/End Subの追加は無かったことにして、

    最初に投稿して頂いたプログラムに基づいて、修正すべき箇所を提案するとすれば・・・

    Private Shared x1 as Double()をPrivate Shared x1 As Double()=New Double(4)と書き換えてみることです。これで、添え字番号0、1、2、3、4の5個の要素からなるDouble配列型の実物ができあがり、それへの参照がx1に格納されます。個々の箱の値へはx1(0)、x1(1)・・・、というように書いてアクセスできることはご存知だと思います。新しくDouble配列型の実物を作ったその瞬間は、全ての要素は0.0の値になっています。これはそういう仕様です。

    で、プログラムを実行してみたら、Form2上のF2TB1(たぶんTextBoxと拝察)に0と表示されるんじゃないかと。

    もう少しイジってみるなら、Form2.ShowDialog()の直前にx1(0)=3.14の1行を追加してみてください。

    で、プログラムを実行してみたら、Form2上のF2TB1(たぶんTextBoxと拝察)に3.14と表示されるんじゃないかと。


    • 編集済み 外池 2019年1月19日 12:12
    2019年1月19日 11:56
  • Newというキーワードには少なくとも2つの使い方があることを知っておいてください。

    「変数名=New クラス名」という使い方が1つです。クラス名(型番)の新しい実物を作って、その実物への参照を変数名に格納する、という動作になります。

    もう1つは、

    コンストラクタと呼ばれるもので、「Class クラス名」~「Enc Class」の間に「Sub New」~「End Sub」という書き方をする使い方です。これはクラス名(型番)の新しいインスタンス(実物)が作られる毎に最初に実行されるサブルーチンになります。

    「Class クラス名」~「Enc Class」の間に「Shared Sub New」~「End Sub」と書けば、最初の最初に1回だけ実行されるサブルーチンになります。インスタンス(実物)を何個作ろうが、1回しか実行されません。

    「Sub New」~「End Sub」と「Share Sub New」~「End Sub」は併記できます。


    • 編集済み 外池 2019年1月19日 12:11
    2019年1月19日 12:10
  • >フォーム2に以下のソースを追加したところ、フォーム1の「Form2.ShowDialog()」の部分に「非共有メンバーを参照するには、オブジェクト参照が必要です」というエラーが生じてしまいました。

    > Sub New(x1() As Double)
    >        F2TB1.Text = x1(0).ToString
    >    End Sub

    コードが間違っているようです。以下のようにすれば望む結果になると思います。

    Form1

    Public Class Form1
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Dim x1 As Double() = New Double(4) {}
            x1(0) = 12345.0
            x1(1) = 67890.0
            Dim f2 As Form2 = New Form2(x1)
            f2.ShowDialog()
        End Sub
    End Class

    Form2

    Public Class Form2
        Public Sub New(ByVal y As Double())
            InitializeComponent()
            F2TB1.Text = y(0).ToString()
        End Sub
    End Class


    2019年1月19日 14:40
  • おんやさい252 さま よろしく。

    単に、Form1 に Button2、Form2 に TextBox F2TB1 を置いただけで、
    Form1 の以下のコードでは如何ですか?。

    Public Class Form1
        Friend x1s As Double() = {1.0, 2.0, 3.0}

        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            x1s(0) = 1.5
        End Sub

        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            'Dim x1s As Double() = {1.0, 2.0, 3.0}
            Dim f2 As New Form2
            f2.F2TB1.Text = x1s(0)
            f2.ShowDialog()
        End Sub
    End Class

    一方通行で渡すだけなら、Form2 に記述は不要です。


    • 編集済み ShiroYuki_Mot 2019年1月19日 14:51 コード変更 値はきっと何処かで処理の筈
    • 回答としてマーク おんやさい252 2019年1月20日 12:08
    2019年1月19日 14:42
  • 外池さん、本当に丁寧な解説ありがとうございました。すべて読ませていただきました。

    今までネットに流れているメソッドなどをただ流用して何が行われているのかもわからずに使っていました。エラー解消も同じようにやっていって解決しても「うごけばいいや」という考えで原因はわからずじまいということも多かったです。そんな僕にとって外池さんの解説は本当に勉強になりました。「オブジェクト指向はjavaやVB等で基本的かつ大事な考え方」であるというのは聞いたことがあるのですが実感せずにいました。今回はオブジェクト指向の重要性、そして知識を深めるとてもいい機会になりました。解説も参考にして問題も解決できました。本当にありがとうございました。

    2019年1月20日 11:51
  • SurferOnWwwさん!わざわざコードまで書いて教えてくださりありがとうございます!こちらのやり方でもうまくいきました!コードも短くでき、新しいやり方も知ることが出来て助かりました。本当にありがとうございました。
    2019年1月20日 12:00
  • ShiroYuki_Motさん、返信ありがとうございます!こちらの方法でもうまく動きました!Form2に記述しないというのは目から鱗でしたがこんな簡単な方法でうまくいって感動しました。とても勉強になりました。ありがとうございましたm(__)m
    2019年1月20日 12:08
  • > Form2に記述しないというのは目から鱗でしたがこんな簡単な方法でうまくいって感動しました。

    それが可能なのはデザイナ画面でコントロールをドラッグ&ドロップすると、そのコントロールのアクセス修飾子が Friend になるという VB.NET の Windows Forms アプリの特殊事情(?)によるものですので注意してください。



    (ちなみに C# は private になります。VB.NET アプリで friend か private かどちらが適切かはいろいろ議論があるかもしれませんが、オブジェクト指向のカプセル化の観点だけから言えば private ではないかと思います)
    2019年1月21日 1:56
  • はい、覚えておきます。最後まで丁寧に教えてくださりありがとうございます。
    2019年1月27日 10:24