構造体の配列同士でデータをコピーするには?

解決


シー  2009-08-31 19:07:35  No: 142474

WindowsXP SP3 + VB.Net2005で開発しています。

以下のような構造体を作成し、
    Public Structure ptypMain
        Friend usrMain() As ptypData
    End Structure

    Public Structure ptypData
        Friend strName As String
        Friend intNo As Integer
    End Structure

その配列を2つ宣言します。
    Public OriginalData() As ptypMain
    Public NowData() As ptypMain

その間のデータをCloneによって受渡していますが、
NowData = OriginalData.Clone

片方のstrNameを変更すると、
もう一方も変更されてしまいます。

構造体の配列にはCloneは機能しないものなのでしょうか?
参照では無く、データをコピーをするための良い方法をご存知でしたら教えて下さい。


YuO  2009-08-31 19:47:35  No: 142475

System.Array.Cloneは,配列の簡易コピー (浅いコピー) を作成します。
MSDN: Array.Clone メソッド (System)
http://msdn.microsoft.com/ja-jp/library/system.array.clone.aspx

このため,ptypMain.usrMainが参照の共有のみされています。
# ptypMain自体は値型のため複製されるものの,配列は参照型のため,usrMainは参照が複製される

対処方法は,自分で作る事のみです。
ptypMainをICloneableにして,Cloneを自分で実装し,
さらにOriginalData.Cloneではなく,すべての要素についてCloneを呼び出すように変更する必要があります。
# Array.ConvertAllあたりでClone返すのもありですが,2008じゃないから使いにくい……。


魔界の仮面弁士  2009-08-31 20:24:11  No: 142476

> 構造体の配列にはCloneは機能しないものなのでしょうか?
ヘルプにも書かれていますように、Array.Clone メソッド は、
Array の『簡易コピー』を作成するためのものです。

それぞれの要素が配列(参照型)であるか構造体(値型)であるかは考慮されず、
それぞれの要素が単純にコピーされるだけです。つまり要素が参照型の場合、
その参照情報がコピーされるだけであって、参照先のオブジェクトまではコピーされません。

いわゆる[シャローコピー(shallow copy)]と呼ばれる動作ですね。

> 片方のstrNameを変更すると、
> もう一方も変更されてしまいます。
参照先のデータもコピーする事を、[ディープ コピー(deep copy)]と呼ぶ事があります。

この場合基本的には、自前で処理するしかありません。
参照情報をコピーする手法は、必ずしも一般化されているわけではないため、
その手法を提示してやる必要があるためです。

たとえばその参照型が、C:\sample.txt を開く FileStream であった場合はどうでしょう。
これをコピーする場合、C:\sample2.txt への FileStream にするべきでしょうか?
それとも、同じ物理ファイルを開く、別の FileStream インスタンスになるべきでしょうか?

また、データベースへの接続オブジェクトだった場合はどうでしょうか?
同一セッション内の別トランザクションになるべきなのか、
別のセッションになるべきなのか、あるいは同一トランザクション内の
別の Connection インスタンスになるべきなのか、非常に曖昧です。

ただしデータ型によっては、『シリアル化』と呼ばれる手法を利用することで、
ディープ コピーを実現することが可能です。
http://msdn.microsoft.com/ja-jp/library/ms973893.aspx
http://shinshu.fm/MHz/88.44/archives/0000234348.html

> 参照では無く、データをコピーをするための良い方法をご存知でしたら教えて下さい。
シリアル化を使わない実装例も紹介しておきます。
http://blogs.wankuma.com/jeanne/archive/2006/04/06/22272.aspx
http://blogs.wankuma.com/jeanne/archive/2006/04/07/22287.aspx


シー  2009-09-01 17:42:41  No: 142477

>YuO様、魔界の仮面弁士様
ありがとうございます。
構造体だからではなく、配列の中に更に配列があるからできないということでしょうか。

教えて頂いたとおり、ICloneableについて調べてみたいと思います。


YuO  2009-09-01 19:43:48  No: 142478

> 構造体だからではなく、配列の中に更に配列があるからできないということでしょうか。

ではなく,「構造体の中に配列がある」から,構造体のコピーでは済まなくなっています。
魔界の仮面弁士さんが書かれている,
>つまり要素が参照型の場合、
>その参照情報がコピーされるだけであって、参照先のオブジェクトまではコピーされません。
の部分がそれです。
# 配列は要素がなんであれ参照型です。


シー  2009-09-15 01:52:01  No: 142479

実際の構造体はもっと複雑なもので、全体を考えるのは大変そうでしたので、必要な部分だけ(データの書換えがあるところだけ)にCloneを用いることで、
思い通りの動作をするようになりました。

本当は、教えて頂いたようなやり方が好ましいのかもしれませんが、
この問題については、もう少し勉強してみたいと思います。

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


※返信する前に利用規約をご確認ください。








  このエントリーをはてなブックマークに追加