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

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

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

Re[3]: なぜ結果が違うのでしょうか?


(過去ログ 116 を表示中)

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

■68597 / inTopicNo.1)  なぜ結果が違うのでしょうか?
  
□投稿者/ 迷える小執事 (1回)-(2013/10/31(Thu) 13:20:53)

分類:[.NET 全般] 

L1とL2の内容が違うのはなぜでしょう?
L1[0] と L2[0] が同じものを指していないから?
でしょう・・

object obj = new object();
List<object> L1 = new List<object>();
List<object> L2 = new List<object>();
L1.Add(obj);
L2.Add(L1[0]);
L1[0] = "test";
// L1=Test; L2=System.Object
MessageBox.Show("L1=" + L1[0].ToString() + "; L2=" + L2[0].ToString());

でも・・
以下は同じ
object obj = new object();
obj = "Test";
List<object> L1 = new List<object>();
List<object> L2 = new List<object>();
L1.Clear();
L1.Add(obj);
L2.Clear();
L2.Add(L1[0]);
// L1=Test; L2=Test
MessageBox.Show("L1=" + L1[0].ToString() + "; L2=" + L2[0].ToString());

理解できません。
教えてください。
引用返信 編集キー/
■68600 / inTopicNo.2)  Re[1]: なぜ結果が違うのでしょうか?
□投稿者/ とっちゃん (166回)-(2013/10/31(Thu) 15:13:24)
とっちゃん さんの Web サイト
No68597 (迷える小執事 さん) に返信
> L1とL2の内容が違うのはなぜでしょう?
> L1[0] と L2[0] が同じものを指していないから?
> でしょう・・
>
> object obj = new object();
> List<object> L1 = new List<object>();
> List<object> L2 = new List<object>();
> L1.Add(obj);
> L2.Add(L1[0]);
> L1[0] = "test";
> // L1=Test; L2=System.Object
> MessageBox.Show("L1=" + L1[0].ToString() + "; L2=" + L2[0].ToString());
>
このコードの、
L1[0] = "test";
の手前でもMessageBox.Show() を表示してみてください。


> でも・・
> 以下は同じ
> object obj = new object();
> obj = "Test";
> List<object> L1 = new List<object>();
> List<object> L2 = new List<object>();
> L1.Clear();
> L1.Add(obj);
> L2.Clear();
> L2.Add(L1[0]);
> // L1=Test; L2=Test
> MessageBox.Show("L1=" + L1[0].ToString() + "; L2=" + L2[0].ToString());
>
こちらは、MessageBox.Show() のうしろに
L1[0] = "test2";
MessageBox.Show("L1=" + L1[0].ToString() + "; L2=" + L2[0].ToString());

としてみてください。
結果を見て、どう感じますか?

引用返信 編集キー/
■68602 / inTopicNo.3)  Re[2]: なぜ結果が違うのでしょうか?
□投稿者/ 迷える小執事 (2回)-(2013/10/31(Thu) 16:09:44)
L1[0] は obj のコピー
L2[0] は L1[0] のコピー
つまり

obj,L1[0],L2[0]
それぞれ別ものってことですよね。


では、obj も L1[0] も L2[0] も同じになるように(参照型)
になるようにするにはどうすればいいのでしょうか?
引用返信 編集キー/
■68603 / inTopicNo.4)  Re[3]: なぜ結果が違うのでしょうか?
□投稿者/ とっちゃん (167回)-(2013/10/31(Thu) 16:53:35)
とっちゃん さんの Web サイト
No68602 (迷える小執事 さん) に返信
> L1[0] は obj のコピー
> L2[0] は L1[0] のコピー
> つまり
>
> obj,L1[0],L2[0]
> それぞれ別ものってことですよね。
>
ここでいうコピーとはなんでしょう?
参照型でコピー(複製)という場合、何を複製しているのかが重要になります。

> では、obj も L1[0] も L2[0] も同じになるように(参照型)
> になるようにするにはどうすればいいのでしょうか?

obj と L1[0], L2[0] が同じものを参照すれば同じになります。
違うものを参照すれば、異なるものとなります。

禅問答のように感じると思いますが、参照型とはそういうもの(ただしかなり乱暴な表現)なので。。。

引用返信 編集キー/
■68609 / inTopicNo.5)  Re[1]: なぜ結果が違うのでしょうか?
□投稿者/ 魔界の仮面弁士 (398回)-(2013/10/31(Thu) 21:43:02)
2013/10/31(Thu) 21:50:22 編集(投稿者)

No68597 (迷える小執事 さん) に返信
> L1とL2の内容が違うのはなぜでしょう?

//--------------
int a = 123;
int b = a;
a = 456;
MessageBox.Show(a.ToString()); // 変数 a の値は 123 から、456 に変更されている。
MessageBox.Show(b.ToString()); // 変数 b の値は 123 のままであり、456 にはならない。

上記は理解できますか?


//--------------
var a = this.textBox1;
var b = a;
a = this.textBox2;
MessageBox.Show(a.Text); // 変数 a が参照するオブジェクトは、textBox1 から textBox2 に変更されている。
MessageBox.Show(b.Text); // 変数 b が参照するオブジェクトは textBox1 のままであり、textBox2 にはなっていない。

上記は理解できますか?


//--------------
var a = this.textBox1;
var b = a;
a = this.textBox2;
a.Text = "xyz";
MessageBox.Show(a.Text); // 変数 a が指す textBox2 に対して、Text が "xyz" に変更されている。
MessageBox.Show(b.Text); // 変数 b が指す textBox1 の Text は書き換えられていない。

上記は理解できますか?


//--------------
var L1 = new List<TextBox>();
var L2 = new List<TextBox>();
L1.Add(this.textBox1);
L2.Add(L1[0]);
L1[0] = this.textBox2;
MessageBox.Show(L1[0].Text); // L1[0] が参照するオブジェクトは、textBox1 から textBox2 に変更されている。
MessageBox.Show(L2[0].Text); // L2[0] が参照するオブジェクトは textBox1 のままであり、textBox2 にはなっていない。

上記は理解できますか?
元質問( No68597 )の前半部で行われている動作は、これに近いですね。


//--------------
var L1 = new List<TextBox>();
var L2 = new List<TextBox>();
L1.Add(this.textBox1);
L2.Add(L1[0]);
L1[0].Text = "xyz";
MessageBox.Show(L1[0].Text); // L1[0] が指す textBox1 に対して、Text が "xyz" に変更されている。
MessageBox.Show(L2[0].Text); // L2[0] も同じ textBox1 を参照しているため、Text は "xyz" と表示される。

上記は理解できますか?


//--------------
var L1 = new List<TextBox>();
var L2 = new List<TextBox>();
L1.Add(this.textBox1);
L2.Add(L1[0]);
L1[0] = this.textBox2;
L1[0].Text = "xyz";
MessageBox.Show(L1[0].Text); // L1[0] が参照する textBox2 に対して、Text が "xyz" に変更されている。
MessageBox.Show(L2[0].Text); // L2[0] が参照するオブジェクトは textBox1 のままなので、Text は変更されない。

上記は理解できますか?
引用返信 編集キー/
■68610 / inTopicNo.6)  Re[2]: なぜ結果が違うのでしょうか?
□投稿者/ 魔界の仮面弁士 (399回)-(2013/10/31(Thu) 21:47:09)
2013/10/31(Thu) 22:05:07 編集(投稿者)

No68609 (魔界の仮面弁士) に追記
> int a = 123;
> int b = a;
> a = 456;
> MessageBox.Show(a.ToString()); // 変数 a の値は 123 から、456 に変更されている。
> MessageBox.Show(b.ToString()); // 変数 b の値は 123 のままであり、456 にはならない。

ついでに、値型と参照型の動作の違いについても。


struct X { public int v; }
class Y { public int v; }

private void button1_Click(object sender, EventArgs e)
{
  var a = new X() { v = 111 };
  var b = a;
  a.v = 222;
  MessageBox.Show(b.v.ToString()); // 111 のまま (X は値型なので)

  var c = new Y() { v = 333 };
  var d = c;
  c.v = 444;
  MessageBox.Show(c.v.ToString()); // 333 → 444 に変更 (Y は参照型なので)

  var obj = new X() { v = 555 };
  var refObject = __makeref(obj);
  __refvalue(refObject, X).v = 666;
  MessageBox.Show(obj.v.ToString()); // 555 → 666 に変更 (型付き参照を通じた変更…まず使わない)
}
引用返信 編集キー/
■68611 / inTopicNo.7)  Re[2]: なぜ結果が違うのでしょうか?
□投稿者/ 迷える小執事 (3回)-(2013/11/01(Fri) 08:42:40)
No68609 (魔界の仮面弁士 さん) に返信
> 2013/10/31(Thu) 21:50:22 編集(投稿者)
>
> ■No68597 (迷える小執事 さん) に返信
>>L1とL2の内容が違うのはなぜでしょう?
>
〜〜〜〜〜<<中略>>〜〜〜〜〜
>
> 上記は理解できますか?

理解できているとおもいます。
理解できないのは

> var L1 = new List<TextBox>();
> var L2 = new List<TextBox>();
> L1.Add(this.textBox1);
> L2.Add(L1[0]);
> L1[0].Text = "xyz";
> MessageBox.Show(L1[0].Text); // L1[0] が指す textBox1 に対して、Text が "xyz" に変更されている。
> MessageBox.Show(L2[0].Text); // L2[0] も同じ textBox1 を参照しているため、Text は "xyz" と表示される。
これなんですけど
TextBoxではなくてObjectを使うとだめなのがわからないんです。

object obj = new object();
List<object> L1 = new List<object>();
List<object> L2 = new List<object>();
L1.Add(obj);
L2.Add(L1[0]);
obj = "xyz";
MessageBox.Show(L1[0].ToString()); // L1[0] が指す obj "xyz" になる?
MessageBox.Show(L2[0].ToString()); // L2[0] が指す L1[0] の指す obj の "xyz" になる?

オブジェクトが理解できていない?
オブジェクトってクラスじゃないのかな?
それ以前の問題なのでしょうか?
引用返信 編集キー/
■68612 / inTopicNo.8)  Re[3]: なぜ結果が違うのでしょうか?
□投稿者/ shu (410回)-(2013/11/01(Fri) 09:03:21)
(迷える小執事 さん) に返信
>>var L1 = new List<TextBox>();
>>var L2 = new List<TextBox>();
>>L1.Add(this.textBox1);
>>L2.Add(L1[0]);
>>L1[0].Text = "xyz";
>>MessageBox.Show(L1[0].Text); // L1[0] が指す textBox1 に対して、Text が "xyz" に変更されている。
>>MessageBox.Show(L2[0].Text); // L2[0] も同じ textBox1 を参照しているため、Text は "xyz" と表示される。
> これなんですけど
> TextBoxではなくてObjectを使うとだめなのがわからないんです。
>
L1[0].Textを書き換えているのであって最初に提示されたのと同じことを行うとすると
L1[0] = this.textBox3
のようになるわけでこれだとL1に入っているのはthis.textBox3でありL2に入っているのはthis.textBox1になるわけです。
その点はobjectだからということではないです。
objectだからということで言えばobjectだと定義されたプロパティとかがないのでTextプロパティへの設定のようなことが
出来ないということです。
つまりL1とL2に同じインスタンスをいれて片方の情報を書き換えたらもう片方の情報も書き換わるといったことをやりたいのであれば
書き換える情報をもったクラスを作りインスタンスの置き換えにならないようにする必要があります。


> L1[0] は obj のコピー
> L2[0] は L1[0] のコピー
> つまり
>
> obj,L1[0],L2[0]
> それぞれ別ものってことですよね。
この書き方だと最初から分かってましたと聞こえます。そうでなければ回答を貰ったことにより
分かった旨を書くべきかと思います。
そして、この全て別のものという解釈は間違ってます。
L2[0]とobjは同じインスタンスです。L1[0]は"test"というインスタンスの代入によりobjとは別のものに変わります。
objの内容を書き換えるのではなくL1[0]自体を書き換えているのです。

引用返信 編集キー/
■68613 / inTopicNo.9)  Re[3]: なぜ結果が違うのでしょうか?
□投稿者/ 魔界の仮面弁士 (400回)-(2013/11/01(Fri) 09:39:47)
No68611 (迷える小執事 さん) に返信
> TextBoxではなくてObjectを使うとだめなのがわからないんです。

型の問題では無いような。
この使い方だと、List<object> でも List<TextBox> でも List<int> でも
object obj; でも TextBox obj; でも int obj; でも同じ結果になるかと。


> object obj = new object();
「new object()」で生成された、object 型のインスタンス X を、
object 型の変数 obj に代入しています。


> List<object> L1 = new List<object>();
> List<object> L2 = new List<object>();
> L1.Add(obj);
L1[0] には、そのインスタンス X が入ります。
変数 obj をセットしているのではなく、変数 obj の中身をセットしているわけですね。


> L2.Add(L1[0]);
L2[0] にも、先のインスタンス X が入ります。
変数 L1[0] の中身をセットしていますね。


> obj = "xyz";
「"xyz"」の文字列リテラルで生成された、string 型のインスタンス Y を、
object 型の変数 obj に代入しています。この時点で、obj は X を見なくなります。


> MessageBox.Show(L1[0].ToString()); // L1[0] が指す obj "xyz" になる?
> MessageBox.Show(L2[0].ToString()); // L2[0] が指す L1[0] の指す obj の "xyz" になる?
変数 obj の中身は、string 型のインスタンス Y に書き換えられましたが、
変数 L1[0] の中身は、object 型のインスタンス X を指したままですし、
変数 L2[0] の中身は、object 型のインスタンス X のままです。




===============================================================
(1) string インスタンスの差し替え
---------------------------------------------------------------
string obj = "bag";
List<object> L1 = new List<object>();
List<object> L2 = new List<object>();
L1.Add(obj);
L2.Add(L1[0]);
obj = "bug"; // 変数 obj が参照しているインスタンスを差し替え
MessageBox.Show(obj.ToString()); // string "bug" … 変更された
MessageBox.Show(L1[0].ToString()); // string "bag" … そのまま
MessageBox.Show(L2[0].ToString()); // string "bag" … そのまま


===============================================================
(2) StringBuilder インスタンスの差し替え
---------------------------------------------------------------
StringBuilder obj = new StringBuilder("bag");
List<object> L1 = new List<object>();
List<object> L2 = new List<object>();
L1.Add(obj);
L2.Add(L1[0]);
obj = new StringBuilder("bug"); // 変数 obj が参照しているインスタンスを差し替え
MessageBox.Show(obj.ToString()); // string "bug" … 変更された
MessageBox.Show(L1[0].ToString()); // string "bag" … そのまま
MessageBox.Show(L2[0].ToString()); // string "bag" … そのまま


===============================================================
(3) インスタンスは差し替えず、参照先の内容のみを差し替え
---------------------------------------------------------------
StringBuilder obj = new StringBuilder("bag");
List<object> L1 = new List<object>();
List<object> L2 = new List<object>();
L1.Add(obj);
L2.Add(L1[0]);
obj[1] = 'u'; // StringBuilder そのものではなく、内容のみ変更
MessageBox.Show(obj.ToString()); // string "bug" … 変更された
MessageBox.Show(L1[0].ToString()); // string "bug" … 変更された
MessageBox.Show(L2[0].ToString()); // string "bug" … 変更された

引用返信 編集キー/
■68617 / inTopicNo.10)  Re[4]: なぜ結果が違うのでしょうか?
□投稿者/ 魔界の仮面弁士 (402回)-(2013/11/01(Fri) 10:32:06)
No68613 (魔界の仮面弁士) に追記
>>> オブジェクトってクラスじゃないのかな?
文脈次第で Yes にも No にも取れるので、厳密な線引きは難しいです。(^^;

たとえば、C# では Int32 は class ではないですが、IL レベルで見れば Int32 も class ですし、
VB.NET では、String は Object ですが、VB6 では String は Object ではなかったりして。


とりあえず、
 「クラス(データ型)を、new 等で実体化(インスタンス化)したものを、オブジェクトと呼ぶ」
という説明でどうでしょうか。



>> 型の問題では無いような。
>> この使い方だと、List<object> でも List<TextBox> でも List<int> でも
>> object obj; でも TextBox obj; でも int obj; でも同じ結果になるかと。

shu さんも書かれていますが、「インスタンスそのものを差し替える」のか、
「インスタンスが指し示す内容を差し替える」のかの違いですね。


ちなみに、string は参照型ですが、
 「内容が変化しない」
 「文字列リテラルで生成」
 「== 演算子は、参照一致(object.ReferenceEquals)ではなく、内容比較の意味」
のような特性があるため、利用する側としては、値型のように扱われることになります。

しかもリテラル文字列については、同じ文字列参照を流用することで
メモリ使用量を抑えるようになっています。(文字列インターン プール)
http://msdn.microsoft.com/ja-jp/library/system.string.intern.aspx
http://msdn.microsoft.com/ja-jp/library/system.reflection.emit.opcodes.ldstr.aspx


一応、string についても、インスタンスは差し替えずに中身だけ書き換える方法が
存在しますが、アンセーフコードを使った特殊な書き方になってしまいます。

unsafe private 
    void button1_Click(object sender, EventArgs e)
{
    string obj = "bag";
    List<object> L1 = new List<object>();
    List<object> L2 = new List<object>();
    L1.Add(obj);
    L2.Add(L1[0]);
    fixed (char* c = obj) { *(c + 1) = 'u'; }
    MessageBox.Show(obj.ToString());    // string "bug" … 変更された
    MessageBox.Show(L1[0].ToString());  // string "bug" … 変更された
    MessageBox.Show(L2[0].ToString());  // string "bug" … 変更された
}

引用返信 編集キー/


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

このトピックに書きこむ

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

管理者用

- Child Tree -