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

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

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

Re[2]: Listへの要素追加


(過去ログ 108 を表示中)

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

■64474 / inTopicNo.1)  Listへの要素追加
  
□投稿者/ anny (1回)-(2012/12/05(Wed) 00:09:31)

分類:[C#] 

開発環境:Visual Studio 2010

リストに要素を追加する際,追加するのに使用した変数の値が変化すると,追加先の値まで変わってしまって困っています.

以下に載せているソースコードでは,
explain()においてはtemp_explainの値が変化しても追加先への影響はないです.
page_explain[0][0].startは500
page_explain[0][1].startは2000

しかし,data()においてはpage_Data[0]の値が変化すると追加先も変化してしまいます.
tab_Data[0][0].startは100
tab_Data[0][1].startは100

どうしてこうなるのかわかりません.
data()においても,追加に使用した変数の値が変更しても影響されないようにしたいのですが,どうすればいいでしょうか.
page_Dataは追加するときのみに使用し,使いまわしたいのです.
どうぞ,よろしくお願いします.

class Program
    {
        public struct Data  
        {            
            public int end;
            public int start;         
        }
        public static Data[] page_Data = new Data[10];  //構造体配列
        public static List<Data[]> tab_Data = new List<Data[]>();      //構造体配列が要素になっているリスト 

        public struct Explain      
        {
            public int start;
            public int end;      
        }

        public static Explain temp_explain;     //構造体
        public static List<Explain>[] page_explain = new List<Explain>[10]; //構造体のリストが配列になっている 
        public static List<List<Explain>[]> tab_explain = new List<List<Explain>[]>();

        static void Main(string[] args)
        {
            for (int i = 0; i < 10; i++)
            {                
                page_explain[i] = new List<Explain>();
            }
            explain();
            data();
        }

        static void data()
        {           
            page_Data[0].start = 1;
            page_Data[0].end = 5;
            tab_Data.Add(page_Data);
            page_Data[0].start = 100;
            page_Data[0].end = 500;
            tab_Data.Add(page_Data);            
        }

        static void explain()
        {
            temp_explain.start = 500;
            temp_explain.end = 1000;
            page_explain[0].Add(temp_explain);
            temp_explain.start = 2000;
            temp_explain.end = 3000;
            page_explain[0].Add(temp_explain);
            tab_explain.Add(page_explain);
        }
    }

引用返信 編集キー/
■64479 / inTopicNo.2)  Re[1]: Listへの要素追加
□投稿者/ 魔界の仮面弁士 (102回)-(2012/12/05(Wed) 02:18:44)
No64474 (anny さん) に返信
> 追加するのに使用した変数の値が変化すると,追加先の値まで変わって
「構造体」は『値型』ですが、「配列」は『参照型』だからです。

ついでに言えば、「List<T>」もクラスなので参照型です。こっちは今回の動作とは無関係ですが。


> explain()においてはtemp_explainの値が変化しても追加先への影響はないです.
「page_explain[0].Add(temp_explain);」を二回行っていますが、
temp_explain が値型なので、一回目の Add と二回目の Add は、
それぞれ別の値を登録したものとみなされる為です。

temp_explain が値型である証拠に、
temp_explain.GetType().IsValueType は true を返し、
temp_explain.GetType().IsClass は false を返します。


なお、一時的な変数を使いまわすことは好ましくないので、今回のようなコードでは、
temp_explain は static にせず、ローカル変数として扱った方がよろしいかと。


> しかし,data()においてはpage_Data[0]の値が変化すると追加先も変化してしまいます.
「tab_Data.Add(page_Data);」を二回行っていますが、
page_Data は参照型です(page_Data[0]は値型ですが)。

page_Data に格納されたインスタンスは new しなおされていないため、
一回目のAddと二回目のAddは、同一インスタンスへの参照を登録したことになります。

page_Data.GetType().IsValueType は false を返し、
page_Data.GetType().IsClass は true を返します。参照型ですね。

同様に、page_Data[0].GetType() の IsValueType / IsClass は
値型の特性を示すことを確認しておいてください。



> page_Dataは追加するときのみに使用し,
.Add(page_Data) で追加する前に、その都度 new Data[10] しなおしておけば、
一回目の Add と二回目の Add とで、それぞれは別のインスタンスとなります。

この場合、new によって新たな構造体配列が出来上がるため、new した直後の個々の要素は、
0クリアされた構造体となります。

もし、0クリアされることをよしとせず、new する以前の情報を残したい場合は、
new する代わりに page_Data.Clone() で複製し、それを Add するという手もあります。
引用返信 編集キー/
■64534 / inTopicNo.3)  Re[2]: Listへの要素追加
□投稿者/ anny (2回)-(2012/12/05(Wed) 21:58:56)
丁寧に解説していただきありがとうございます.
恥ずかしながら,今まであまり値型とか参照型とかを意識せずにコードを書いていました.
魔界の仮面弁士様のおかげで理解が深まりました.

また,構造体配列をnewすることにより,思い通りの動作になりました.
解決済み
引用返信 編集キー/


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

このトピックに書きこむ

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

管理者用

- Child Tree -