none
構造体内部の配列について RRS feed

  • 質問

  • 以下のサンプルで、構造体内部の配列をクリアします(a.Dat2(0)="")と、

    別の配列(b.Dat2(0)="")までクリアされてしまいます。配列以外、例えば

    b.Dat1はa.Dat1=""にしてもクリアされません。これはVBの不具合でしょうか?

    回避方法を教えてください。よろしくお願いします。

    Structure aType
        Dim Dat1 As String
        Dim Dat2() As String
        Dim Dat3 As String
    End Structure

    Public Class Form1
        Dim a As aType
        Dim b As aType

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

            ReDim a.Dat2(3)
            ReDim b.Dat2(3)

            a.Dat1 = "1"
            a.Dat2(0) = "2"
            a.Dat2(1) = "2"
            a.Dat3 = "3"

            b = a

            a.Dat1 = ""
            a.Dat2(0) = ""
            a.Dat3 = ""

        End Sub
    End Class

     

    2006年4月19日 9:50

回答

  •  V5 さんからの引用
    Dim a As aType
    Dim b As aType


    Private a As New aType()
    Private b As New aType()

     

    にしたらどうなりますか。

    2006年4月19日 10:12
  • 構造体の代入は値のコピーが行われる……というのがよくある説明です。ではこの値とは何でしょうか。

    構造体(値型)の場合、値とは各メンバのそれぞれの値全てです。構造体をコピーすると、各メンバの値がそれぞれコピーされます。今回のサンプルである aType 型では、Dat1 の値、Dat2 の値、Dat3 の値がそれぞれコピーされます。

    配列は参照型です。参照型における値とは、そのインスタンスの参照の事を指します。つまり、構造体に含まれる配列は、その構造体を別の変数に代入した際、その配列の参照がコピーされることになります。

    これはつまり、配列の実体は変わらないと言うことになります。コピー元とコピー先は、参照がコピーされるだけで同じものを指しています。コピー元で配列の内容を操作したら、当然コピー先でも(同じ配列の実体を参照しているのですから)変更されます。

    配列の各要素をコピーするのなら、Array.Clone メソッドなどを使う必要があります。

    では Dat1 はどうでしょうか。String も参照型(Class で宣言されている)ですから、コピー元とコピー先は同じ実体を参照しています。ここでコピー元に空文字列を代入すると、それはつまり別の実体を代入すると言うことになります。コピー元とコピー先は別の実体を持つことになりますから、変更は伝播しません。

    同じように、配列でも、新しく確保した配列(や Array.Clone で作ったコピー)を代入すると、それからは別の実体を参照することになるので、片方の変更がもう片方に影響しなくなります。

    2006年4月19日 11:19
  •  V5 さんからの引用
    Array.Clone メソッドを使用とありますが、
    ヘルプなど見ましたが、使用方法がいまいちです。



    b.Dat2 = DirectCast(a.Dat2.Clone(), String())

     

    多分、こんな感じでしょう。

    2006年4月19日 22:47
  •  V5 さんからの引用
    サンプルで、教えていただければ幸いです。

    Cloneable なサンプルならば、これ。



        Private Structure aType
            Implements System.ICloneable

            Public Dat1 As String
            Public Dat2 As String()
            Public Dat3 As String

            Public Function DeepCopy() As aType
                Dim cloneObject As aType = DirectCast(Me.Clone(), aType)

                cloneObject.Dat2 = DirectCast(Me.Dat2.Clone(), String())

                Return cloneObject
            End Function

            Private Function Clone() As Object Implements System.ICloneable.Clone
                Return Me.MemberwiseClone()
            End Function

            Public Overrides Function ToString() As String
                Dim prompt As String = String.Empty

                prompt &= Dat1 & ", "

                For Each st As String In Me.Dat2
                    prompt &= st & ", "
                Next st

                prompt &= Dat3

                Return prompt
            End Function

        End Structure

        Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim a As aType
            a.Dat1 = "a-Dat1"
            a.Dat2 = New String() {"a-Dat2-1", "a-Dat2-2", "a-Dat2-3"}
            a.Dat3 = "a-Dat3"

            Dim b As aType = a.DeepCopy()
            b.Dat1 = "b-Dat1"
            b.Dat2(0) = "b-Dat2-1"
            b.Dat2(1) = "b-Dat2-2"
            b.Dat2(2) = "b-Dat2-3"
            b.Dat3 = "b-Dat3"

            MessageBox.Show(a.ToString())
            MessageBox.Show(b.ToString())
        End Sub

     

    それにしても、構造体ではなくクラスを使用することを検討した方が良いです。

    2006年4月20日 0:24
  • VS2005のヘルプからの引用
    (1)Array の簡易コピーでは、要素が参照型であるか値型であるかに関係なく、その Array の要素だけがコピーされ、
       それらの参照が指している先のオブジェクトはコピーされません。
    (2)新しい Array 内の参照は、元の Array 内の参照と同じオブジェクトを指します。

    上記(1)と(2)の言ってる事は逆ではないでしょうか?

    同じ現象で悩みました。結局構造体内の配列メンバーをコピーする場合は、cloneメソッドで解決しました。
    でも、上記ヘルプの意味がいまだに、マニュアルの不具合と読んでしまうのは、私だけでしょうか??
    2年間悩み続けています。
    すっきりさせてください。 ビル・ゲイツ様!!

    2009年12月21日 20:04
  • 上記(1)と(2)の言ってる事は逆ではないでしょうか?
    いいえ、合っています。
    「元の Array 内の参照と同じオブジェクトを指します」ということと、「それらの参照を指している先のオブジェクトはコピーされません」ということは、この場合では同じことを言っています。

    参照型とは何かについて、もう一度ご確認ください。
    (配列は参照型です)

    あと、同じ現象で悩んだ結果、新しいことを提起していますよね?(「ドキュメントは間違いではないか?」ということ)
    それであれば、このスレッドの URL を添えて、新しいスレッドを立ててください。

    すっきりさせてください。 ビル・ゲイツ様!!
    念のため。
    ここは Microsoft の公式見解が聞ける場所ではありません。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2009年12月21日 22:31
    モデレータ

すべての返信

  •  V5 さんからの引用
    Dim a As aType
    Dim b As aType


    Private a As New aType()
    Private b As New aType()

     

    にしたらどうなりますか。

    2006年4月19日 10:12
  • 構造体の代入は値のコピーが行われる……というのがよくある説明です。ではこの値とは何でしょうか。

    構造体(値型)の場合、値とは各メンバのそれぞれの値全てです。構造体をコピーすると、各メンバの値がそれぞれコピーされます。今回のサンプルである aType 型では、Dat1 の値、Dat2 の値、Dat3 の値がそれぞれコピーされます。

    配列は参照型です。参照型における値とは、そのインスタンスの参照の事を指します。つまり、構造体に含まれる配列は、その構造体を別の変数に代入した際、その配列の参照がコピーされることになります。

    これはつまり、配列の実体は変わらないと言うことになります。コピー元とコピー先は、参照がコピーされるだけで同じものを指しています。コピー元で配列の内容を操作したら、当然コピー先でも(同じ配列の実体を参照しているのですから)変更されます。

    配列の各要素をコピーするのなら、Array.Clone メソッドなどを使う必要があります。

    では Dat1 はどうでしょうか。String も参照型(Class で宣言されている)ですから、コピー元とコピー先は同じ実体を参照しています。ここでコピー元に空文字列を代入すると、それはつまり別の実体を代入すると言うことになります。コピー元とコピー先は別の実体を持つことになりますから、変更は伝播しません。

    同じように、配列でも、新しく確保した配列(や Array.Clone で作ったコピー)を代入すると、それからは別の実体を参照することになるので、片方の変更がもう片方に影響しなくなります。

    2006年4月19日 11:19
  • じゃんぬねっと様、

    お世話になります。V5です。

    試してみましたが、やはりクリアされてしまいます。

    他に回避方法はないでしょうか?

    2006年4月19日 11:23
  •  V5 さんからの引用
    b = a

    あら、やだ。

    ここを逃していましたね... orz

    2006年4月19日 12:19
  • Hongliang様

    お世話になります。V5です。

    勉強になります。Array.Clone メソッドを使用とありますが、

    ヘルプなど見ましたが、使用方法がいまいちです。

    サンプルで、教えていただければ幸いです。

    よろしく、お願い致します。

     

    2006年4月19日 19:07
  •  V5 さんからの引用
    Array.Clone メソッドを使用とありますが、
    ヘルプなど見ましたが、使用方法がいまいちです。



    b.Dat2 = DirectCast(a.Dat2.Clone(), String())

     

    多分、こんな感じでしょう。

    2006年4月19日 22:47
  •  V5 さんからの引用
    サンプルで、教えていただければ幸いです。

    Cloneable なサンプルならば、これ。



        Private Structure aType
            Implements System.ICloneable

            Public Dat1 As String
            Public Dat2 As String()
            Public Dat3 As String

            Public Function DeepCopy() As aType
                Dim cloneObject As aType = DirectCast(Me.Clone(), aType)

                cloneObject.Dat2 = DirectCast(Me.Dat2.Clone(), String())

                Return cloneObject
            End Function

            Private Function Clone() As Object Implements System.ICloneable.Clone
                Return Me.MemberwiseClone()
            End Function

            Public Overrides Function ToString() As String
                Dim prompt As String = String.Empty

                prompt &= Dat1 & ", "

                For Each st As String In Me.Dat2
                    prompt &= st & ", "
                Next st

                prompt &= Dat3

                Return prompt
            End Function

        End Structure

        Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim a As aType
            a.Dat1 = "a-Dat1"
            a.Dat2 = New String() {"a-Dat2-1", "a-Dat2-2", "a-Dat2-3"}
            a.Dat3 = "a-Dat3"

            Dim b As aType = a.DeepCopy()
            b.Dat1 = "b-Dat1"
            b.Dat2(0) = "b-Dat2-1"
            b.Dat2(1) = "b-Dat2-2"
            b.Dat2(2) = "b-Dat2-3"
            b.Dat3 = "b-Dat3"

            MessageBox.Show(a.ToString())
            MessageBox.Show(b.ToString())
        End Sub

     

    それにしても、構造体ではなくクラスを使用することを検討した方が良いです。

    2006年4月20日 0:24
  • じゃんぬねっと様

    お世話になります。V5です。

    参考にさせていただきます。

    ありがとう、ございました。

    2006年4月20日 9:45
  • じゃんぬねっと様

    お世話になります。V5です。

    非常に悩んでいましたが、おかげさまで解決できました。

    以下がサンプルコードです。

    ありがとう、ございました。

    Structure aType
        Dim Dat1 As String
        Dim Dat2() As String
        Dim Dat3 As String
    End Structure

    Public Class Form1
        Dim a As aType
        Dim b As aType

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            ReDim a.Dat2(3)
            ReDim b.Dat2(3)

            a.Dat1 = "1"
            a.Dat2(0) = "2"
            a.Dat2(1) = "2"
            a.Dat2(2) = "2"
            a.Dat2(3) = "2"
            a.Dat3 = "3"

            b = a
            b.Dat2 = DirectCast(a.Dat2.Clone(), String())

            a.Dat1 = ""
            a.Dat2(0) = ""
            a.Dat2(1) = ""
            a.Dat2(2) = ""
            a.Dat2(3) = ""
            a.Dat3 = ""

        End Sub
    End Class

    2006年4月20日 15:43
  • VS2005のヘルプからの引用
    (1)Array の簡易コピーでは、要素が参照型であるか値型であるかに関係なく、その Array の要素だけがコピーされ、
       それらの参照が指している先のオブジェクトはコピーされません。
    (2)新しい Array 内の参照は、元の Array 内の参照と同じオブジェクトを指します。

    上記(1)と(2)の言ってる事は逆ではないでしょうか?

    同じ現象で悩みました。結局構造体内の配列メンバーをコピーする場合は、cloneメソッドで解決しました。
    でも、上記ヘルプの意味がいまだに、マニュアルの不具合と読んでしまうのは、私だけでしょうか??
    2年間悩み続けています。
    すっきりさせてください。 ビル・ゲイツ様!!

    2009年12月21日 20:04
  • 上記(1)と(2)の言ってる事は逆ではないでしょうか?
    いいえ、合っています。
    「元の Array 内の参照と同じオブジェクトを指します」ということと、「それらの参照を指している先のオブジェクトはコピーされません」ということは、この場合では同じことを言っています。

    参照型とは何かについて、もう一度ご確認ください。
    (配列は参照型です)

    あと、同じ現象で悩んだ結果、新しいことを提起していますよね?(「ドキュメントは間違いではないか?」ということ)
    それであれば、このスレッドの URL を添えて、新しいスレッドを立ててください。

    すっきりさせてください。 ビル・ゲイツ様!!
    念のため。
    ここは Microsoft の公式見解が聞ける場所ではありません。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2009年12月21日 22:31
    モデレータ