例えば一見するとうまく動きそうなコードがある。DataGridViewから複数行を選択して削除するコードである。
var c = from DataGridViewRow r in dataGridView1.Rows where r.Selected select r;
DataRow dr;
foreach (DataGridViewRow dgvr in c)
{
dr = ((DataRowView)dgvr.DataBoundItem).Row;
dr.Delete();
}
しかし、上のコードがうまく動かない。cにはDataGridViewRowのコレクションが入っているように思うかもしれないが、そうではないからである。実は、
foreach (DataGridViewRow dgvr in c)
を実行する度に
var c = from DataGridViewRow r in dataGridView1.Rows where r.Selected select r;
に制御が戻り、そこからDataGridViewRowが取得されているからである。return yieldのような動作をしているというと理解しやすいかもしれない。
したがって、コレクションをなめている時にそのコレクションの中身が変わってしまうと、当然、うまく動かなくなってしまう。
cにはDataGridViewRowのコレクションが入っており、それをなめながらDataRowを削除しているのだからうまくはずだと思ってしまうのだが、実際にはDataGridViewRowのコレクションが毎回作成し直されてしまうことに注意しなければならない。
したがって、上記は以下のようにDataGridViewRowのコレクションを一度保存しておくとうまくいく。
var c = from DataGridViewRow r in dataGridView1.Rows where r.Selected select r;
List<DataGridViewRow> list = c.ToList<DataGridViewRow>();
DataRow dr;
foreach (DataGridViewRow dgvr in list)
{
dr = ((DataRowView)dgvr.DataBoundItem).Row;
dr.Delete();
}
LINQ to Objectは、データベースのSQL文のように、結果セットを一度に取ってきて終わりというわけではないことに注意しよう。