none
参照型の値コピー

    質問

  • winxpsp2

    vs2005c#

     

    //コード------------------------------------------------------------------------

    public struct MyStruct {
        public string Mystr1;
        public string[] Mystr2;
    }


    private void Form1_Load(object sender, EventArgs e) {
        MyStruct[] test1 = new MyStruct[3];
        test1[0].Mystr2 = new string[3];
        test1[0].Mystr1 = "AAA";
        test1[0].Mystr2[0] = "BBB";

        MyStruct[] test2 = (MyStruct[])test1.Clone();//①

        test2[0].Mystr1 = "CCC";          //②
        test2[0].Mystr2[0] = "DDD";      //③
    }

    //コード------------------------------------------------------------------------

     

    参照型の値のコピーを作成しようとしています。

    ①でClone()メソッドを使用しているので、②で値を変更してもtest1[0].Mystr1 の値は変わりませんが、②でtest1[0].Mystr2[0] の値が変わってしまいます。

    test1の参照ではなく値の完全なコピーをtest2に作成するにはどのようにすればよいのでしょうか?

    ①の後にtest2[0].Mystr2 = (string[])test1[0].Mystr2.Clone();とするしかないのでしょうか?

    知っている方がいましたら宜しくお願いします。

     

     

    2006年12月15日 7:54

回答

すべての返信

  • MyStruct に Clone() メソッドを定義するのが常套でしょうけど、代替手段として、 シリアル化可能なクラスならば MemoryStream 等に一度シリアル化してから拾い上げれば可能だと思います。
    2006年12月15日 8:47
  • MSshingo さん、こんにちは。

     MSshingo さんからの引用
    (1) でClone()メソッドを使用しているので、(2) で値を変更してもtest1[0].Mystr1 の値は変わりませんが、(2) でtest1[0].Mystr2[0] の値が変わってしまいます。

    「シャローコピー」 と 「ディープ コピー」 について調べてみましょう。

    2006年12月15日 10:00
  • 現状、シリアル化で対応していますが違和感を感じています。

     

    そこでClone()メソッドを実装してみました。

    変更前

    public struct MyStruct {
        public string Mystr1;
        public string[] Mystr2;
    }

    変更後

    public struct MyStruct {
        public string Mystr1;
        public string[] Mystr2;

        public MyStruct DeepCopy() {
            MyStruct cloneObject = (MyStruct)this.Clone();

            if (this.Mystr2 != null) {
                cloneObject.Mystr2 = (string[])this.Mystr2.Clone();
            }
            return cloneObject;
        }

        private Object Clone() {
            return this.MemberwiseClone();
        }
    }

    下記スレッドを参考にしました。

    http://forums.microsoft.com/MSDN-JA/ShowPost.aspx?PostID=356450&SiteID=7

    サンプルがVBなのでC#への変換方法が不明でとりあえず動くように変換しただけといった感じなので間違えがあると思います。(指摘して頂けると幸いです。)

     

    しかしこれでは次の様なコピーとなってしまいます。

    MyStruct[] test2 = new MyStruct[3];
    for (int cnti = 0; cnti < test2.Length; cnti++) {
        test2[cnti] = test1[cnti].DeepCopy();
    }

     

    test2 = test1."MyStruct構造体に実装した何らかのコピーメソッド"()

    上記の様な形でのコピーを行ないたいのですが可能でしょうか?

    可能であればどのように構造体内にコーディングするのか教えていただけないでしょうか

    宜しくお願いします。

    2006年12月15日 10:03
  • 「シャローコピー」 と 「ディープ コピー」について調べてみました。

    参照型を値コピーしても参照型内の参照型は値がコピーされない「シャローコピー」 ようで、自信で値コピー「ディープ コピー」を実装しないといけないということですね。

    囚人さんに返信した内容は構造体内で「ディープ コピー」を行なっているつもりなのですが(普通は”ICloneable”を実装するのでしょうか?)

    それにしても構造体を配列として宣言して、全ての値コピー「ディープ コピー」を行なうには配列要素一つ一つに対して行なうようにコピーしようとしているところでコーディングしなければならないのでしょうか?

    test2 = test1."MyStruct構造体に実装した何らかのコピーメソッド"()

    上記の様な形が無理であれば、シリアル化でと考えていますが、参照型のコピーにシリアル化を利用するというのは一般的に利用される一つの方法と考えていいのでしょうか?

    2006年12月15日 10:43
  • 青柳です。

    test1 の型は MyStruct[] なんですよね?この場合、test1 は Array のインスタンスであって、MyStruct のインスタンスではありません。なので、MyStruct に定義したメソッドを

    > test2 = test1."MyStruct構造体に実装した何らかのコピーメソッド"()

    という風に呼び出すことはできないわけです。

    class MyStruct
    {
        public static MyStruct[] Clone(MyStruct[] x)
        {
             ...
        }
    }

    というような static メソッドを用意して
        MyStruct[] test2 = MyStruct.Clone(test1);
    という感じではどうでしょう?

    2006年12月15日 10:45
  •  Shinichi Aoyagi さんからの引用

    test1 の型は MyStruct[] なんですよね?この場合、test1 は Array のインスタンスであって、MyStruct のインスタンスではありません。なので、MyStruct に定義したメソッドを

    >

    test1 の型は配列です。

    私がイメージしていた様には実装できないようですが、1行で初期化から値コピーまで行なえるということですね。

    public static MyStruct[] Clone(MyStruct[] x)
        {
             ...
        }

    このメソッド内で

    public MyStruct DeepCopy() {
            MyStruct cloneObject = (MyStruct)this.Clone();

            if (this.Mystr2 != null) {
                cloneObject.Mystr2 = (string[])this.Mystr2.Clone();
            }
            return cloneObject;
        }

        private Object Clone() {
            return this.MemberwiseClone();
        }

    を実行するイメージでしょうか?

    今回、シリアル化と構造体内にメソッドを実装する方法どちらにするか考えていますが、メソッドだとメンバに変更があった際に修正が入り、構造体毎にメソッドを実装する必要があると思います。

    シリアル化だと構造体メンバの変更や新たに構造体を作成した場合にもシリアル化部分の変更は発生しなと考えて、シリアル化の方がコーディングが容易ではと考えています。

    気になるのはパフォーマンスともう一つ、人に見せた場合に「何でこんなところでシリアル化なの!」という声が上がらないかです。その時にシリアル化で対応する方法は「これも一般的に用いられる方法だよ」といえる方法ならばいいんですが。

    2006年12月15日 11:16
  • >それにしても構造体を配列として宣言して、全ての値コピー「ディープ コピー」を行なうには配列要素一つ一つに対して行なうようにコピーしようとしているところでコーディングしなければならないのでしょうか?

    そうです。

    >上記の様な形が無理であれば、シリアル化でと考えていますが、参照型のコピーにシリアル化を利用するというのは一般的に利用される一つの方法と考えていいのでしょうか?

    そうです。

    .NETの構造体はC++の構造体のようにmemcpyで一気にコピーできるようなものではないのです。

    基本はすべて自分で面倒をみます。

    2006年12月15日 15:41
  • みなさん、レス頂きありがとうございます。

    Clone()メソッド、シリアル化、場合によって使い分けしたいと思います。

    2006年12月18日 0:43