DOBON.NET DOBON.NETプログラミング掲示板過去ログ

構造体について

  • 題名: 構造体について
  • 著者: イルカ
  • 日時: 2004/03/03 10:39:17
  • ID: 2831
  • この記事の返信元:
    • (なし)
  • この記事への返信:
  • ツリーを表示
環境/言語:[Win2000 VB.NET Framework1.1]
分類:[.NET]

構造体について質問です。
現在以下のような構造体を作成しています。
Structure udtPress
Dim strIpAdress As String '// IP Adress
Dim intPort As Integer '// TCP/IP

Dim strPressName As String
Dim intDouColor() As Color_ID
Public Sub Initialize()
ReDim intDouColor(8)
End Sub
End Structure

そして、以下の様に宣言しています。
Public wwA as udtPress
Public wwB as udtPress

wwAを初期化して使用していてある処理でwwBに代入しています。
wwB = wwA

代入した後、wwBで構造体のメンバの値を変更すると、元のwwAのメンバの
値も変更されてしまいます。

変数名が違っても、参照している構造体が一緒なので片方を変更するともう
片方のデータも変更されてしまうのですか?
教えて下さい。お願い致します。
> Dim intDouColor() As Color_ID

Color_ID型はどういう型ですか?

> Public wwA as udtPress
> Public wwB as udtPress
>
> wwAを初期化して使用していてある処理でwwBに代入しています。
> wwB = wwA
>
> 代入した後、wwBで構造体のメンバの値を変更すると、元のwwAのメンバの
> 値も変更されてしまいます。

どのメンバを変更した場合に変更されていますか?
例えば、wwBのintPortだけを変えた場合ならwwAのintPortには何も影響しないはずです。

> 変数名が違っても、参照している構造体が一緒なので片方を変更するともう
> 片方のデータも変更されてしまうのですか?

影響を受けているのは、intDouColor() の各要素ではありませんか?

> 教えて下さい。お願い致します。

値型と参照型について調べてみて下さい。
■No2834に返信(よねKENさんの記事)
> Color_ID型はどういう型ですか?

Public Enum Color_ID
Cyan = 0
Magenta = 1
Black = 2
Yellow = 3
Spot = 4
End Enum

> 影響を受けているのは、intDouColor() の各要素ではありませんか?

intDouColor()の部分が影響を受けています。

> 値型と参照型について調べてみて下さい。

ヘルプを見てみたのですがいまいちよく分かりません。
どのようにしたらよいのでしょうか?
■No2835に返信(イルカさんの記事)
構造体が値型というのは分かったのですが、
コピーを行った後に互いに影響を受けないようにすることは
できないのでしょうか?
■No2857に返信(イルカさんの記事)
> ■No2835に返信(イルカさんの記事)
> 構造体が値型というのは分かったのですが、
> コピーを行った後に互いに影響を受けないようにすることは
> できないのでしょうか?

>wwAを初期化して使用していてある処理でwwBに代入しています。
>wwB = wwA

この処理で実際には何が「コピー」されているのか調べてみてください。
wwBにwwAの「値」がコピーされているのでしょうか?
私も同じ目に合い苦労しました。

構造体は値型でも、構造体中にある配列は値ではなく参照が
コピーされるようですね。

そこで、私は構造体のメンバの値を一つ一つコピーすることによって、
なんとか回避しました。

Wiht wwB
.strIpAdress = wwA.strIpAdress
.intPort = wwA.strIpAdress
.strPressName = wwA.strPressName
Redim .intDouColor(wwA.intDouC.Length - 1)
For i As Integer = 0 To .intDouColor.Length - 1
.intDouColor(i) = wwA.intDouColor(i)
Next
End With

といった感じで(^ ^;)

私も.Net(オブジェクト指向)初心者なのでこれが正しい
方法なのかわかりませんが、少なくともコピー先の
変数の変更の影響をコピー元が受けることはなくなりました。
理由はMoMoMoさんのご指摘の通りです。
ちなみにString型も参照型ですが、こちらはImmutable(不変)なオブジェクトとして
設計・実装されているため、ほとんどの場合で値型と同じように使用することができます。

で、こういうときは、ICloneableインタフェースのCloneメソッドを実装するのが
正当なやり方になると思います。
(CopyToのようなメソッドを独自に実装するのでもいいわけですが、
内部の実装としては同じ形になります)

(実装例)

Public Enum Color_ID
Cyan = 0
Magenta = 1
Black = 2
Yellow = 3
Spot = 4
End Enum

' 改良版udtPress ICloneableインタフェースを実装します。
Public Structure udtPress
Implements ICloneable

Dim strIpAdress As String '// IP Adress
Dim intPort As Integer '// TCP/IP

Dim strPressName As String
Dim intDouColor() As Color_ID
Public Sub Initialize()
ReDim intDouColor(8)
End Sub

' テスト結果を見るためにToStringメソッドを実装しておきます。
Public Overrides Function ToString() As String
Dim s As New System.Text.StringBuilder
s.AppendFormat("{0}{1}", Me.GetType(), ControlChars.CrLf)
s.AppendFormat("{0}strIpAddress={1}{2}", ControlChars.Tab, Me.strIpAdress, ControlChars.CrLf)
s.AppendFormat("{0}intPort={1}{2}", ControlChars.Tab, Me.intPort, ControlChars.CrLf)
s.AppendFormat("{0}strPressName={1}{2}", ControlChars.Tab, Me.strPressName, ControlChars.CrLf)
s.AppendFormat("{0}intDouColor={1}{2}", ControlChars.Tab, Me.intDouColor, ControlChars.CrLf)
If Not intDouColor Is Nothing Then
For i As Integer = 0 To intDouColor.Length - 1
s.AppendFormat("{0}intDouColor({1})={2}{3}", ControlChars.Tab, i, Me.intDouColor(i), ControlChars.CrLf)
Next
End If

Return s.ToString()
End Function

' ICloneableインタフェースのCloneメソッドを実装
' Dim c As ICloneable = u ' uはudtPress型の変数
' Dim o As Object = c.Clone() ' ICloneable.Cloneメソッドを使ったコピー
Private Function ICloneableClone() As Object Implements System.ICloneable.Clone
Return Me.Clone()
End Function

' 通常udtPress型として、各変数を扱うと思いますが、
' このときにICloneable.Cloneメソッドは戻り値がObject型のため、
' 使用が面倒ですので、udtPress型のCloneメソッドを定義します。
Public Function Clone() As udtPress
Dim newValue As New udtPress

newValue.strIpAdress = Me.strIpAdress
newValue.intPort = Me.intPort
newValue.strPressName = Me.strPressName

If Not Me.intDouColor Is Nothing Then
newValue.intDouColor = New Color_ID(Me.intDouColor.Length - 1) {}
For i As Integer = 0 To Me.intDouColor.Length - 1
newValue.intDouColor(i) = Me.intDouColor(i)
Next
End If

Return newValue
End Function
End Structure

'' コピーのテスト用コード
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim noInitialize As udtPress ' 何も初期化しない場合のToStringのテスト用
Dim ShallowCopySrc As New udtPress ' wwwB = wwwA方式のテスト用のコピー元
Dim DeepCopySrc As New udtPress ' wwwB = wwwA.Clone()方式のテスト用のコピー元

Dim ShallowCopyDst As udtPress ' wwwB = wwwA方式のテスト用のコピー先
Dim DeepCopyDst As udtPress ' wwwB = wwwA.Clone()方式のテスト用のコピー先

' 単なる代入方式、Cloneメソッドによるコピー、それぞれのコピー元の変数に初期設定をします。
ShallowCopySrc.Initialize()
ShallowCopySrc.strIpAdress = "255.255.255.255"
ShallowCopySrc.intPort = 8080
ShallowCopySrc.strPressName = "なまえ"

ShallowCopySrc.intDouColor(0) = Color_ID.Magenta
ShallowCopySrc.intDouColor(1) = Color_ID.Black
ShallowCopySrc.intDouColor(2) = Color_ID.Yellow
ShallowCopySrc.intDouColor(3) = Color_ID.Spot

DeepCopySrc.Initialize()
DeepCopySrc.strIpAdress = "255.255.255.255"
DeepCopySrc.intPort = 8080
DeepCopySrc.strPressName = "なまえ"

DeepCopySrc.intDouColor(0) = Color_ID.Magenta
DeepCopySrc.intDouColor(1) = Color_ID.Black
DeepCopySrc.intDouColor(2) = Color_ID.Yellow
DeepCopySrc.intDouColor(3) = Color_ID.Spot


Debug.WriteLine("コピーに関するテスト start")

Debug.WriteLine("-- 何も値を設定していない場合のToString() start ------------")
Debug.WriteLine(noInitialize.ToString())
Debug.WriteLine("-- 何も値を設定していない場合のToString() end ------------")

Debug.WriteLine("-- コピー処理 start ------------")
ShallowCopyDst = ShallowCopySrc ' シャローコピー
DeepCopyDst = DeepCopySrc.Clone() ' ディープコピー
Debug.WriteLine("-- コピー処理 end ------------")

Debug.WriteLine("-- コピー直後 start ------------")
Debug.WriteLine("シャローコピー元:" & ShallowCopySrc.ToString())
Debug.WriteLine("シャローコピー先:" & ShallowCopyDst.ToString())
Debug.WriteLine("ディープコピー元:" & DeepCopySrc.ToString())
Debug.WriteLine("ディープコピー元:" & DeepCopyDst.ToString())
Debug.WriteLine("-- コピー直後 end ------------")

Debug.WriteLine("-- コピー後にコピー先のデータを変更する start ------------")
ShallowCopyDst.strIpAdress = "シャローコピー先のIP"
ShallowCopyDst.intDouColor(0) = Color_ID.Black
DeepCopyDst.strIpAdress = "ディープコピー先のIP"
DeepCopyDst.intDouColor(0) = Color_ID.Black
Debug.WriteLine("-- コピー後にコピー先のデータを変更する end ------------")

Debug.WriteLine("-- データ変更後後 start ------------")
Debug.WriteLine("シャローコピー元:" & ShallowCopySrc.ToString())
Debug.WriteLine("シャローコピー先:" & ShallowCopyDst.ToString())
Debug.WriteLine("ディープコピー元:" & DeepCopySrc.ToString())
Debug.WriteLine("ディープコピー元:" & DeepCopyDst.ToString())
Debug.WriteLine("-- データ変更後 end ------------")

Debug.WriteLine("コピーに関するテスト end ")
End Sub
  • 題名: 参照型の配列の場合どうすんの?
  • 著者: どーでもいいことだが
  • 日時: 2004/03/07 18:32:43
  • ID: 2915
  • この記事の返信元:
  • この記事への返信:
    • (なし)
  • ツリーを表示


>私も同じ目に合い苦労しました。
>少なくともコピー先の変数の変更の影響をコピー元が受けることはなくなりました
なんか言語仕様がおかしいっと言ってるように聞こえる・・
  • 題名: Re[7]: 構造体について
  • 著者: イルカ
  • 日時: 2004/03/08 8:53:40
  • ID: 2919
  • この記事の返信元:
  • この記事への返信:
    • (なし)
  • ツリーを表示
■No2914に返信(よねKENさんの記事)
MoMoMoさん、よねKENさんありがとうございます。
時間がなかったので大変でしたけど自分もMoMoMoさんのようにして作成しました。
よねKENさんの例を実装例をしっかりとよみ今後生かしていきたいと思います。
みなさんありがとうございました。
また、わからないことが発生したら質問させていただきますので
よろしくお願い致します。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板