C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[2]: MemberwiseClone() を用いた配列のコピー


(過去ログ 124 を表示中)

[トピック内 8 記事 (1 - 8 表示)]  << 0 >>

■73844 / inTopicNo.1)  MemberwiseClone() を用いた配列のコピー
  
□投稿者/ Kotatsu (1回)-(2014/11/09(Sun) 21:37:52)

分類:[.NET 全般] 

こんにちは。
以前に何度か質問させていただいたことがあるものです。

MemberwiseClone() によって、Individual[0]のGeneの中身をEliteにコピーしようとしました。
MemberwiseClone() を用いると、コピー元を変更してもコピーされたものに関しては変更されないとのことなので、
以下のコードのように、Individual[0]のGeneの中身をEliteにコピーし、その後にIndividual[0]の中身を変更し、
Individual[0]の中身と、Eliteの中身を出力してみたところ、Eliteの中身もIndividualと同様に変更されていしまっているようでした。
このように、Eliteの中身も一緒に書き換わってしまった原因を教えて頂けると助かります。
わかりにくいかもしれませんがよろしくお願いします。

using System;

public struct GeneType :ICloneable
{
    public string[] Gene;
    public int Fitness;

    public GeneType(int geneNumber, int Fitness = 0)
    {
        this.Gene = new string[geneNumber];
        this.Fitness = Fitness;
    }

    public object Clone()
    {
        return MemberwiseClone();
    }
}

class Compose
{
    public static void Main()
    {
        string[] Note = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p" };

        Random random = new Random();

        int geneNumber = 16;
        int indNumber = 20;

        GeneType[] Individual = new GeneType[indNumber];

        for (int i = 0; i < indNumber; i++)
        {
            Individual[i] = new GeneType(geneNumber);
            for (int j = 0; j < geneNumber; j++)
            {
                int RandomNumber = random.Next(Note.Length);
                Individual[i].Gene[j] = Note[RandomNumber];
            }
        }

        GeneType Elite = new GeneType();
        Elite = (GeneType)Individual[0].Clone();//EliteにIndividual[0]をコピー

        //Individual[0]の中身を表示
        for (int i = 0; i < geneNumber; i++)
        {
            Console.Write(Individual[0].Gene[i]);
        }
        Console.WriteLine("");

        //Eliteの中身を表示
        for (int i = 0; i < geneNumber; i++)
        {
            Console.Write(Elite.Gene[i]);
        }
        Console.WriteLine("");

        //Individual[0]の中身を変更
        for (int i = 0; i < geneNumber; i++)
        {
            int RandomNumber = random.Next(Note.Length);
            Individual[0].Gene[i] = Note[RandomNumber];
        }

        //再びIndividual[0]の中身を表示
        for (int i = 0; i < geneNumber; i++)
        {
            Console.Write(Individual[0].Gene[i]);
        }
        Console.WriteLine("");

        //再びEliteの中身を表示
        for (int i = 0; i < geneNumber; i++)
        {
            Console.Write(Elite.Gene[i]);
        }
        Console.WriteLine("");
    }
}

引用返信 編集キー/
■73845 / inTopicNo.2)  Re[1]: MemberwiseClone() を用いた配列のコピー
□投稿者/ WebSurfer (376回)-(2014/11/09(Sun) 23:15:18)
マルチポストのようですので、別掲先のページのリンクを貼っておきます。

https://social.msdn.microsoft.com/Forums/ja-JP/f93a5540-f6bf-46cd-86f3-b38ec6349930/memberwiseclone-?forum=vsgeneralja


質問者さんへ>

マルチポストは歓迎されないということはご存知ですか? 個人的にはマルチポスト
は絶対にダメとは思ってませんが、その旨きちんと書くなど、最低限のマナーは必要
だと思いますが・・・

引用返信 編集キー/
■73846 / inTopicNo.3)  Re[1]: MemberwiseClone() を用いた配列のコピー
□投稿者/ WebSurfer (377回)-(2014/11/09(Sun) 23:19:04)
No73844 (Kotatsu さん) に返信

あともう一つ。以下のページの質問者さんと同じ方(ちょとすさん)ですよね?

http://bbs.wankuma.com/index.cgi?mode=al2&namber=73809

であれば同じ名前を使ってください。
引用返信 編集キー/
■73847 / inTopicNo.4)  Re[2]: MemberwiseClone() を用いた配列のコピー
□投稿者/ ちょとすさん (1回)-(2014/11/09(Sun) 23:26:30)
質問する際のマナーを心得ていませんでした。
申し訳ありませんでした。

調べていてもなかなか問題点を解決できずにいたため、2つの掲示板に書き込んでしまいました。
本当にすみませんでした。
引用返信 編集キー/
■73848 / inTopicNo.5)  Re[1]: MemberwiseClone() を用いた配列のコピー
□投稿者/ Azulean (362回)-(2014/11/09(Sun) 23:49:50)
No73844 (Kotatsu さん) に返信

シャローコピーとディープコピーについて学んでいただいた方が良いでしょう。
MemberwiseClone メソッドの解説にも書いてありますが、「フィールドが参照型の場合、参照はコピーされますが、参照先オブジェクトはコピーされないため、元のオブジェクトとその複製は同じオブジェクトを参照します」です。
配列型は参照型なので、MemberwiseClone では同じ参照を共有するだけとなります。

http://msdn.microsoft.com/ja-jp/library/system.object.memberwiseclone

参照の先まで複製したいのであれば、ディープコピーとして自分で実装する必要があります。
今回の例であれば new string[] で新しい配列を作ってその中身を1つずつコピーするなど、その型に応じたコピー処理を実装する必要があります。
引用返信 編集キー/
■73849 / inTopicNo.6)  Re[3]: MemberwiseClone() を用いた配列のコピー
□投稿者/ ちょとすさん (2回)-(2014/11/09(Sun) 23:52:31)
名前を変更しました。

試してみて、Eliteの中身が変化しなかったコードを以下に載せます。

using System;

public struct GeneType : ICloneable
{
    public string[] Gene;
    public int Fitness;

    public GeneType(int geneNumber, int Fitness = 0)
    {
        this.Gene = new string[geneNumber];
        this.Fitness = Fitness;
    }

    public object Clone()
    {
        return MemberwiseClone();
    }
}

class Compose
{
    public static void Main()
    {
        string[] Note = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p" };

        Random random = new Random();

        int geneNumber = 16;
        int indNumber = 20;

        GeneType[] Individual = new GeneType[indNumber];

        for (int i = 0; i < indNumber; i++)
        {
            Individual[i] = new GeneType(geneNumber);
            for (int j = 0; j < geneNumber; j++)
            {
                int RandomNumber = random.Next(Note.Length);
                Individual[i].Gene[j] = Note[RandomNumber];
            }
        }

        GeneType Elite = new GeneType(geneNumber);

        //EliteにIndividual[0]をコピー
        for (int i = 0; i < geneNumber; i++)//<----------------------修正点
        {
            Elite.Gene[i] = (string)Individual[0].Gene[i].Clone();
        }

        //Individual[0]の中身を表示
        for (int i = 0; i < geneNumber; i++)
        {
            Console.Write(Individual[0].Gene[i]);
        }
        Console.WriteLine("");    

        //Eliteの中身を表示
        for (int i = 0; i < geneNumber; i++)
        {
            Console.Write(Elite.Gene[i]);
        }
        Console.WriteLine("");

        //Individual[0]の中身を変更
        for (int i = 0; i < geneNumber; i++)
        {
            int RandomNumber = random.Next(Note.Length);
            Individual[0].Gene[i] = Note[RandomNumber];
        }

        //再びIndividual[0]の中身を表示
        for (int i = 0; i < geneNumber; i++)
        {
            Console.Write(Individual[0].Gene[i]);
        }
        Console.WriteLine("");

        //再びEliteの中身を表示
        for (int i = 0; i < geneNumber; i++)
        {
            Console.Write(Elite.Gene[i]);
        }
        Console.WriteLine("");
    }
}

引用返信 編集キー/
■73850 / inTopicNo.7)  Re[4]: MemberwiseClone() を用いた配列のコピー
□投稿者/ Azulean (363回)-(2014/11/10(Mon) 07:32:02)
No73849 (ちょとすさん さん) に返信
> 試してみて、Eliteの中身が変化しなかったコードを以下に載せます。

疑問は解決されたということでしょうか?
そうであれば、それを明確にしていただけるとわかりやすくて助かります。

> public object Clone()
> {
> return MemberwiseClone();
> }
> }
(略)
> Elite.Gene[i] = (string)Individual[0].Gene[i].Clone();

現状、Clone をシャローコピーと定義し、ディープコピーすべき要素を外部でコピーするように実装されている点が気になりました。
これで悪いとは言いませんが、どうせならディープコピーのメソッドを GenType クラス側に実装して、利用者は「ディープコピーがしたい!」というメソッドの呼び出しだけにしてみませんか?
そうすれば、GenType が参照型のフィールドを増やすような実装変更をしても、利用する側のコードを触らなくて済む可能性が高まります。
引用返信 編集キー/
■73865 / inTopicNo.8)  Re[2]: MemberwiseClone() を用いた配列のコピー
□投稿者/ ちょとすさん (3回)-(2014/11/10(Mon) 18:12:18)
問題点については改善されましたが、Azulean様の書き込みのように、
「ディープコピーのメソッドを GenType クラス側に実装して、利用者は「ディープコピーがしたい!」というメソッドの呼び出しだけにする」
この点に関して改善したいと考えています。
ですので「解決済み」にはせずに、シャローコピーとディープコピーについてもう少し学習し、改善したらまた書き込みたいと思います。
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -