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

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

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

Re[10]: foreach について


(過去ログ 112 を表示中)

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

■66120 / inTopicNo.1)  foreach について
  
□投稿者/ ouh (1回)-(2013/04/02(Tue) 15:48:13)

分類:[C#] 

以下のコードで、コメント A の行の条件式が一度も ture にならなかったときに、コメント B の行以下が実行されません。
(true になった場合は実行されます)
これはどうしてなんでしょうか?
lst は List<string>、dgv は DataGridView、fnames は List<string> です。

bool b = false;
foreach (var fn in lst) {
	b = false;
	foreach (DataGridViewRow r in dgv.Rows) {
		if (fn == r.Cells["FileName"].Value.ToString()) { // A
			b = true;
			break;
		}
	}
	MessageBox.Show("aaa"); // B
	if (!b) {
		fnames.Add(fn);
	}
}


VS 2010 Express
Win Vista

引用返信 編集キー/
■66122 / inTopicNo.2)  Re[1]: foreach について
□投稿者/ howling (327回)-(2013/04/02(Tue) 15:53:50)
2013/04/02(Tue) 15:56:13 編集(投稿者)


No66120 (ouh さん) に返信
こんにちわ。

うーん、このコードだけだと、正しく通りそうな感じですね。

とすると、原因は何か。

1.lstの中身が何もないので、そもそも2つ目のforeachに到達していない
2.2つ目のforeachの中で例外が起きているが、catchできていないのでスルーしている
3.そもそもそのコードがコンパイルされていない。別のファイルを見てしまっている

こんなところですかね。

ところで、「コメント B の行以下」と書いてあるということは、
Bの行も通ってないということですよね?
その前提で書いてます。

まぁ、いずれにしてもデバッグしてください。ブレークポイント仕掛けて、F10押して行けばわかるんじゃないでしょうか。

ではでは。
引用返信 編集キー/
■66123 / inTopicNo.3)  Re[2]: foreach について
□投稿者/ ouh (2回)-(2013/04/02(Tue) 16:29:47)
No66122 (howling さん) に返信

ありがとうございます。

lst が空であるという可能性を考えて、1 つ目の foreach の前に
MessageBox.Show(lst.Count.ToString());
を入れた上でデバッグしてみると、なんとも不思議な結果になってしまいました。

lst が null だという例外 (NullReferenceException) が出ます。
ですが、上記のメッセージボックスは 1 と表示します。
デバッグで各変数の値を見ても、fn には文字列が入っていますし、lst.Count は 1 になっていて
lst[0] には fn と同じ文字列が入っています。

なんにせよ、lst が null だと (あるいは null ということにされていると) いうことが
わかりました。

こうなるとコードここに載せているコードだけではここをご覧の方々には判断のしようがないと思われます。
ですが、前後が長大であり、それをすべて載せるわけにも参りません。
返信くださった howling さんには申し訳ないのですが、ひとまず「解決済み」として閉めたいと思います。

> ところで、「コメント B の行以下」と書いてあるということは、
> Bの行も通ってないということですよね?
おっしゃるとおりです。
解決済み
引用返信 編集キー/
■66135 / inTopicNo.4)  Re[3]: foreach について
□投稿者/ howling (329回)-(2013/04/02(Tue) 19:00:50)
No66123 (ouh さん) に返信

> MessageBox.Show(lst.Count.ToString());
> lst が null だという例外 (NullReferenceException) が出ます。

やはりそうでしたか。
デバッグ中に例外が出てもtry〜catchしていない場合、

 1.その例外が出たけれども問題が無いのでそのまま進む
 2.その例外が出たので、その関数から抜ける(というか、少なくともその先を追えなくなってしまう)
 3.その例外が解決できず、エラーとして中断する

大体こんな挙動を示すことが多いです。
特に2の場合は、出力ウィンドウに「hogeExceptionの例外が発生しました」といったような記述が追加されるだけで動作してしまうので、
その後の動作が思っている挙動を示さなかったり、デバッグでステップ実行していくと止まってしまう現象に見舞われます。
…といったバグによく直面していると、なんとなく例外が出てるんじゃないかな?と予想ができるようになってきますよ!

> ですが、前後が長大であり、それをすべて載せるわけにも参りません。
> 返信くださった howling さんには申し訳ないのですが、ひとまず「解決済み」として閉めたいと思います。

いえいえ、これはよくある事です。
できるだけ最小コードにまとめて質問するのは正しいことだと思いますし、
そうやって少しずつ進む物なんじゃないでしょうか。
全部載せて、やりたいことだけ述べて「これが質問です」という方がたまにいますが、
それは質問ではなく、仕事になってしまいますので…。

> おっしゃるとおりです。

良かったです。その前提で話していましたので。
また何かあったら今回のように、「これで同じ問題が出る」といった最小コードを書いて頂けると助かります。
(まぁ、今回の場合はどんなデータが入った場合かのテストケースがないので、動作検証もできなかったですが)

ではでは。
解決済み
引用返信 編集キー/
■66137 / inTopicNo.5)  Re[3]: foreach について
□投稿者/ shu (258回)-(2013/04/03(Wed) 07:51:13)
No66123 (ouh さん) に返信
>> lst が null だという例外 (NullReferenceException) が出ます。
> ですが、上記のメッセージボックスは 1 と表示します。
> デバッグで各変数の値を見ても、fn には文字列が入っていますし、lst.Count は 1 になっていて
> lst[0] には fn と同じ文字列が入っています。
>
> なんにせよ、lst が null だと (あるいは null ということにされていると) いうことが
> わかりました。
lstがnullなのにlst.Countの評価が出来るわけがないのでこの挙動自体に
矛盾があります。状況を正確に書かれた方がよいかと思います。lstがnullのケースが正常な動作として
ありうるならif でnull時の処理を分ければよいです。nullのケースが異常であればlstをnullにしている
箇所を探せば原因がわかるかと思います。


> こうなるとコードここに載せているコードだけではここをご覧の方々には判断のしようがないと思われます。
> ですが、前後が長大であり、それをすべて載せるわけにも参りません。
> 返信くださった howling さんには申し訳ないのですが、ひとまず「解決済み」として閉めたいと思います。
解決していないのに済にするのは後でこのスレを見た人に誤解を招かせるのでやめた方がよいかと
思います。長大なコードをすべて載せる必要はなくlstの内容に関わる部分だけ抜粋すると分かりやすいのではないでしょうか?
引用返信 編集キー/
■66142 / inTopicNo.6)  Re[4]: foreach について
□投稿者/ ouh (3回)-(2013/04/03(Wed) 14:24:23)
howling さん、shu さん

失礼しました。

中途半端に終わらせたこと、お詫びします。
また、皆様に間違った情報をお見せしていたことをお詫びします。

NullReferenceException が出る箇所は
if (fn == r.Cells["FileName"].Value.ToString()) {
の行なので、lst が null だと言って例外を出しているのではないようです。

結論から言いますと、null であるのは r.Cells["FileName"].Value.ToStoring() です。
DataGridView の AllowUserToAddRows プロパティが true の場合に Rows を使って
foreach をすると、末尾に付いている新規行までループが回るようです。
また、AllowUserToAddRows が ture の場合、RowCount プロパティは現在の行数 + 1 になる
ようです。データベース上のレコード数と DataGridView 上の行数が 1 違ってしまうという
ことになりますね。
ですので、AllowUserToAddRows が true の場合に foreach を使ってはダメだと言えそうです。
for を使って、
for (int r = 0; r < dgv.RowCount - 1; ++r) で回さないと、行の追加用の行まで
見に行って、そこは当然全カラムが null なのでその値を使おうとすると NullReference
ということのようです。
試しに AllowUserToAddRows を false にしたところ、意図したとおりに動きました。
ユーザーに新規レコードの追加を許可したい場合は全行ループは for 文を使って最後の行まで
行かないように走査しないといけません。


質問の最初から最後まで思い違いばかりしていたようです。
howling さん、shu さんにはたいへん感謝しています。
実は諦めて try - catch で握りつぶしていました。
「解決済み」のあとのお二人のレスがなかったら、そのまま行っているところでした。
2つのケースのコードです。


// DataGridView の AllowUserToAddRows が true の場合
// fnlist:List<string>
// b:bool
// fnames:List<string>
foreach (var fn in fnlist) {
	b = false;
        // 追加用の行があるので for 文でこのように。
	for (int r = 0; r < dgv.RowCount - 1; r++) {
		if (fn == dgv.Rows[r].Cells["FileName"].Value.ToString())
			b = true;
			break;
		}
	}
	if (!b) {
		fnames.Add(fn);
	}
}


// DataGridView の AllowUserToAddRows が false の場合
// fnlist:List<string>
// b:bool
// fnames:List<string>
foreach (var fn in fnlist) {
	b = false;
        // 普通に foreach で OK
	foreach (DataGridViewRow r in dgv.Rows) {
		if (fn == r.Cells["FileName"].Value.ToString()) {
			b = true;
			break;
		}
	}
	if (!b) {
		fnames.Add(fn);
	}
}

解決済み
引用返信 編集キー/
■66143 / inTopicNo.7)  Re[5]: foreach について
□投稿者/ とっちゃん (97回)-(2013/04/03(Wed) 14:45:07)
とっちゃん さんの Web サイト
No66142 (ouh さん) に返信
> 結論から言いますと、null であるのは r.Cells["FileName"].Value.ToStoring() です。

> DataGridView の AllowUserToAddRows プロパティが true の場合に Rows を使って
> foreach をすると、末尾に付いている新規行までループが回るようです。
> また、AllowUserToAddRows が ture の場合、RowCount プロパティは現在の行数 + 1 になる
> ようです。データベース上のレコード数と DataGridView 上の行数が 1 違ってしまうという
> ことになりますね。

これって、r.Cells["FileName"] != null か、r.Cells["FileName"].Value != null が必要なのでは?
新規行の"FileName"セルには、まだ「何も入っていない」ですよね?

そのセルに何もない状態があるということを意識しているかどうかの問題と思います。
DataGridView を使ったことがないので、どこの段階でnullが帰ってくるのかわかりませんが...

ほかの項目が null になることがないのなら、for でも問題ないと思うので、あえて解決済みはチェックしておきます。


解決済み
引用返信 編集キー/
■66144 / inTopicNo.8)  Re[6]: foreach について
□投稿者/ ouh (4回)-(2013/04/03(Wed) 15:08:39)
No66143 (とっちゃん さん) に返信

ご指摘ありがとうございます。
ですが、AllowUserToAddRows が false である場合には新規追加用の行が
ありませんのでそのような必要はありません。
ですがそうですね。そのほうが汎用的ですね。それなら AllowUserToAddRows を
true にしなければならなくなった場合にコードを書き換えないで済みますね。
解決済み
引用返信 編集キー/
■66146 / inTopicNo.9)  Re[7]: foreach について
□投稿者/ howling (331回)-(2013/04/03(Wed) 17:08:11)
No66144 (ouh さん) に返信

ユーザーに新規行追加可能な状態にすると、*印のついた行ができて、
これでズレてしまうということ自体は知っていました。

ですので、foreachを使う場合には(rows.Count - 1) != rows.IndexOf(targetRow)みたいなのを噛ませてた気が。
まぁ、使う側からしたら良いのですが、ぶっちゃけ新規行は分けてくれた方がエラーは起きずに済みますよね…。
これは前に使ってみて、私も何度か同じ例外に当たっていたのでわかります…。

実はぬるり様が出ていたところを言われて、「あぁ、やっぱりか」と思った節があります。
「lstがnullだったのかー、意外だなー」という感想でしたので、なんかスッキリしました。
あぁいう書き方をする場合、nullチェックとDBNullチェックを噛ませていると、
DataGridではエラーが発生しづらい感覚でいますよ。

# null = DBNullと思っている方の投稿をたまに(よく?)見かけますので、
# 実はDBNullとか使ったことがないのですが、そういう認識ができました。

よくあるエラーですから、あんまり気にせず解決していってください!嫌でも覚えます(笑
解決済み
引用返信 編集キー/
■66147 / inTopicNo.10)  Re[8]: foreach について
□投稿者/ howling (332回)-(2013/04/03(Wed) 17:12:15)
すみません、もう1つ追記で。

r.Cells["FileName"].Value.ToString()

この書き方をした場合、
r.Cells["FileName"]がnullの場合と、
r.Cells["FileName"].Valueがnullの場合があります。

いずれも同じエラーで死にますので、特にテーブルを読み込んでデータが入っていない場合とか、
文字列ではなく数字が入っている前提で書いている場合とかでよく死にます。

…とはいえ、そこは当たって死んでみるとわかりやすくていいかもしれませんね。何事も経験です!

ではでは。
解決済み
引用返信 編集キー/
■66148 / inTopicNo.11)  Re[8]: foreach について
□投稿者/ ouh (5回)-(2013/04/03(Wed) 17:13:45)
No66146 (howling さん) に返信

結局、一番楽チンなのは、

foreach (var r in dgv.Rows) {
    if (r.IsNewRow) continue;
   ...
}

みたいです。
もう少し事前に DataGridView を勉強しておくべきでした。

解決済み
引用返信 編集キー/
■66149 / inTopicNo.12)  Re[9]: foreach について
□投稿者/ ouh (6回)-(2013/04/03(Wed) 17:18:50)
No66147 (howling さん) に返信

書いていたら howling さんの追記が。

FileName は DB 上では primary key ですのでここで FileName が null になる
可能性は考慮しないつもりです。
ですので、もし null だったりしたら、むしろ例のダイアログを出して落ちて欲しいのです。
解決済み
引用返信 編集キー/
■66154 / inTopicNo.13)  Re[10]: foreach について
□投稿者/ shu (262回)-(2013/04/03(Wed) 22:12:20)
2013/04/03(Wed) 22:12:35 編集(投稿者)

No66149 (ouh さん) に返信
> ■No66147 (howling さん) に返信
>
> 書いていたら howling さんの追記が。
>
> FileName は DB 上では primary key ですのでここで FileName が null になる
> 可能性は考慮しないつもりです。
> ですので、もし null だったりしたら、むしろ例のダイアログを出して落ちて欲しいのです。
primary keyならなおさら書き込む前にnullかチェックをして書き込みエラーが出ないように
するべきかと思います。

ここにきて思ったのですがそもそもDataGridViewにバインドしているデータがあればDataGridViewから
データを拾ってチェックするのではなくバインドデータでチェックされた方がよい気がします。

解決済み
引用返信 編集キー/


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

このトピックに書きこむ

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

管理者用

- Child Tree -