DOBON.NET DOBON.NETプログラミング掲示板過去ログ

参照型の変数を値型として使う方法?

環境/言語:[win7 visual studio 2008 c#]
分類:[.NET]

VisualStudio2008 C# を使っています。
タイトルが適切な表現なのはわからないのですが…

次のようなコードで処理を実行すると意図しない動作になります。

DataGridViewRow ターゲット行データ = dgv.Rows[ターゲット行];
dgv.Rows[ターゲット行].Cells[1].Value = dgv.Rows[入れ替え行].Cells[1].Value;
dgv.Rows[入れ替え行].Cells[1].Value = ターゲット行データ.Cells[1].Value;

ターゲット行データが参照型なので、元データが変更されると影響を受ける。
からだと思います。

影響を受けないようにターゲット行データを切り離してコピーしたいのですが、
その方法がわかりません。

DataGridViewRowに限らず、XElement などでも、よく同じ悩みに直面します。

表現として正しいのか判りませんが、参照型の変数を値型として使う方法を教えてください。

よろしくお願いします。
> DataGridViewRow ターゲット行データ = dgv.Rows[ターゲット行];
DataGridViewRow ではなく その Cells[1].Value をいったん受け取っておけば良いんでは?
■No27666に返信(muuuuさんの記事)
> DataGridViewRow ターゲット行データ = dgv.Rows[ターゲット行];
行を入れ替えるのではなく、データを入れ替えるのですから、
 DataGridViewCell cell1 = dgv[1, ターゲット行];
 DataGridViewCell cell2 = dgv[1, 入れ替え行];
 object tmp = cell1.Value;
 cell1.Value = cell2.Value;
 cell2.Value = tmp;
とすべきかと。(確認していませんけれども)


あと、データバインドしている場合にも注意が必要です。

たとえば、DataGridView に DataSet/DataTable をバインドしている場合、
入れ替え対象のセルが主キーの一部だった場合、入れ替えの最中に
一意制約違反を起こす可能性があります。そのため、このような場合には
入れ替えの前に、DataSet の EnforceConstraints プロパティを使って、
一時的に制約を Off にしておく必要があるでしょう。
Hongliangさん、ありがとうございます。

ひとつひとつ取り出すとできますね。
ただ実際のコラム数がすごく多いので、一度に処理できないかな?と思いました。

今回の質問は「参照型の扱い方」を知りたいのが一番の理由です。
我が儘でスミマセン。

よろしくお願いします。
仮面弁士さん、いつもありがとうございます。

参照型と値型でいつもトラブってしまいます。
理解不足が原因です。

やはり参照型を簡単に扱うのは難しいのでしょうか。
データの数が多くなるほどに、値まで降りてくるのが
ちょっと大変だなと思い、全体をコピーできないかと
思っています。

よろしくお願いします。
■No27670に返信(muuuuさんの記事)
> 理解不足が原因です。
流れを追ってみましょうか。

>> DataGridViewRow ターゲット行データ = dgv.Rows[ターゲット行];
変数に「タ―ゲット行」への参照が入りますよね。両者は同じものなので、
「ターゲット行データ」に対して何か操作を行った場合、それは
「dgv.Rows[ターゲット行]」に対しても影響を与えます。逆もしかり。


という事は、その後に実行した
>> dgv.Rows[ターゲット行].Cells[1].Value = dgv.Rows[入れ替え行].Cells[1].Value;
この処理は、結局のところ、
 ターゲット行データ.Cells[1].Value = dgv.Rows[入れ替え行].Cells[1].Value;
と同じ意味になってしまうというわけです。

つまりこの時点で、
 (    タ―ゲット行    ).Cells[1].Value
 dgv.Rows[ターゲット行].Cells[1].Value
 dgv.Rows[ 入れ替え行 ].Cells[1].Value
は、全て同じ値になっているわけですね。


> やはり参照型を簡単に扱うのは難しいのでしょうか。
特に難しいと感じていないので、これについてはコメントできません。(^^;


> データの数が多くなるほどに、値まで降りてくるのが
値を入れ替えるのですから、値を取得しないと。
(データバインドしている場合は、バインド元を入れ替える形でも良いですが)


たとえば、フォームに GroupBox を 2 つ貼って、
それぞれに TextBox を載せてください。下記の階層構造にします。

 Form
 ├groupBox1
 │└textBox1
 ├groupBox2
 │└textBox2
 ├button1
 ├button2
 └dataGridView1

さて、ここで下記のようなコードを書いてみます。
先ほどの DataGridViewRow の時と同じような結果になりますが、
それぞれの違いは分かりますか?


int 元 = 1;
int 先 = 3;
private void button1_Click(object sender, EventArgs e)
{
    object tmp = dataGridView1[1, 元].Value;
    dataGridView1[1, 元].Value = dataGridView1[1, 先].Value;
    dataGridView1[1, 先].Value = tmp;

    string s = textBox1.Text;
    textBox1.Text = textBox2.Text;
    textBox2.Text = s;
}

private void button2_Click(object sender, EventArgs e)
{
    DataGridViewRow tmp = dgv.Rows[元];
    dataGridView1.Rows[元].Cells[1].Value = dataGridView1.Rows[先].Cells[1].Value;
    dataGridView1.Rows[先].Cells[1].Value = tmp.Cells[1].Value;

    GroupBox gb = groupBox1;
    groupBox1.Controls["textBox1"].Text = groupBox2.Controls["textBox2"].Text;
    groupBox2.Controls["textBox2"].Text = gb.Controls["textBox1"].Text;
}

private void Form1_Load(object sender, EventArgs e)
{
    dataGridView1.ColumnCount = 3;
    dataGridView1.Rows.Add(11, 12, 13);
    dataGridView1.Rows.Add(21, 22, 23);
    dataGridView1.Rows.Add(31, 32, 33);
    dataGridView1.Rows.Add(41, 42, 43);
    dataGridView1.Rows.Add(51, 52, 53);
    dataGridView1.AutoResizeColumns();
    textBox1.Text = "aaa";
    textBox2.Text = "bbb";
}
魔界の仮面弁士さん、ありがとうございます。

コードを試してみました。
button1の場合はきちんと入れ替わりました。

Value値で変数に格納すると「値型」、その手前(?)だと「参照型」になる。

という感じでしょうか。


さきほど参照型が難しいと書きましたが、
魔界の仮面弁士さんのコメントで、はたと気が付きました。
「参照型は手間が掛かる」が本音でした。^^;
楽しようとした結果、悩むことになった感じです。


XEelementでもよく悩むのでテストコードを作ってみました。


private void button1_Click(object sender, EventArgs e)
{
XElement element2 = element1;
element2.Value = "222";

groupBox1.Controls["textBox1"].Text = element1.Value; //result => 222
groupBox2.Controls["textBox2"].Text = element2.Value; //result => 222
}
private void button2_Click(object sender, EventArgs e)
{
XElement element2 = new XElement("root");
element2.Value = element1.Value;
element2.Value = "222";

groupBox1.Controls["textBox1"].Text = element1.Value; //result => 111
groupBox2.Controls["textBox2"].Text = element2.Value; //result => 222
}

private void button3_Click(object sender, EventArgs e)
{
XElement element2 = new XElement(element1);
element2.Value = "222";

groupBox1.Controls["textBox1"].Text = element1.Value; //result => 111
groupBox2.Controls["textBox2"].Text = element2.Value; //result => 222
}

button1が、これまで勘違いしていた方法で参照型をそのままコピー
button2が、今回教えていただいたValueを使い値型として扱う
button3が、実は少し混乱している部分です。

new XEelement(element1) の扱いがいつもモヤモヤしています。

element1は参照型、new XElementも参照型。

コピーではなく…element1が値として扱われて…モヤモヤ。
何となく腑に落ちない感じが実はトラブルの原因かも知れません。
■No27673に返信(muuuuさんの記事)
> new XEelement(element1) の扱いがいつもモヤモヤしています。
>
> element1は参照型、new XElementも参照型。
>
> コピーではなく…element1が値として扱われて…モヤモヤ。
new XEelement(element1)の書き方がXEelementのインスタンスを新たに作成し
element1の内容をコピーしているので値(型?)として扱われているとかでは
ないです。コピーコンストラクタというやつです。
>>コピーではなく…element1が値として扱われて…モヤモヤ。
> new XEelement(element1)の書き方がXEelementのインスタンスを新たに作成し
> element1の内容をコピーしているので値(型?)として扱われているとかでは
> ないです。コピーコンストラクタというやつです。

そうですね。
値ではなくて、新たに作られている感じですね。
コピーというのが、中身のコピーと、参照場所のコピーと
勘違いしそうです。

もっと勉強するようにします。
解決で閉じます。
皆さんありがとうございます。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板