質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Q&A

解決済

2回答

14702閲覧

foreachでのListでnullを回避する別の方法とは

P5_USER

総合スコア73

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

0グッド

0クリップ

投稿2018/01/10 10:17

編集2018/01/12 08:38

いつもお世話になっています。

以下の参考にしたものに書いてあります動画を元に写経をしてアレンジしました。
実行すると,foreachの行でエラーを吐かれてしまって右往左往の状態です。

何故,このような状態が起きているのでしょうか。
どのコードを変更あるいは追加するのが一番良いのでしょうか。

ちなみにSaveFile()はボタンクリックイベントで実行されるようになっています。

###参考にしたもの
C# Tutorial - Read & Write csv file | FoxLearn
写経元は以下のようになっています。
Studentクラスはデータグリッドビューのデータソースの選択から指定しています。

using CsvHelper; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace ReadWriteCSV { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void btnWrite_Click(object sender, EventArgs e) { using (SaveFileDialog sfd = new SaveFileDialog() { Filter = "CSV|*.csv", ValidateNames = true }) { if (sfd.ShowDialog()==DialogResult.OK) { using (var sw = new StreamWriter(sfd.FileName)) { var writer = new CsvWriter(sw); writer.WriteHeader(typeof(Student)); foreach(Student s in studentBindingSource.DataSource as List<Student>) { writer.WriteRecord(s); } } MessageBox.Show("Your data has been successfully saved.", "Message", MessageBoxButtons.OK, MessageBoxIcon.Information); } } } private void Form1_Load(object sender, EventArgs e) { studentBindingSource.DataSource = new List<Student>(); } private void btnRead_Click(object sender, EventArgs e) { using (OpenFileDialog ofd = new OpenFileDialog() { Filter = "CSV|*.csv", ValidateNames = true }) { if (ofd.ShowDialog()==DialogResult.OK) { var sr = new StreamReader(new FileStream(ofd.FileName, FileMode.Open)); var csv = new CsvReader(sr); //studentBindingSource.DataSource = csv.GetRecord<Student>().ToString(); studentBindingSource.DataSource = csv.GetRecords<Student>(); } } } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ReadWriteCSV { public class Student { public string StudentID { get; set; } public string StudentName { get; set; } public string Email { get; set; } public string Phone { get; set; } } }

###発生している問題・エラーメッセージ
foreachの行で発生。

System.NullReferenceException: 'オブジェクト参照がオブジェクト インスタンスに設定されていません。' (... as System.Collections.Generic.List<KMAP.AirPortDataClass>) が null を返しました。

イメージ説明
イメージ説明
イメージ説明
###該当のソースコード

C#

1 /// <summary> 2 /// CSVのファイルパスを指定して保存する 3 /// </summary> 4 /// <param name="filePath">CSVのファイルパスを指定します</param> 5 private void SaveFile(string filePath) 6 { 7 using (var sw = new StreamWriter(filePath, false, Encoding.GetEncoding("SHIFT-JIS"))) 8 { 9 var writer = new CsvWriter(sw); 10 writer.WriteHeader(typeof(AirPortDataClass)); 11 foreach (AirPortDataClass AirPortData in airPortDataClassBindingSource.DataSource as List<AirPortDataClass>) 12 { 13 writer.WriteRecord(AirPortData); 14 } 15 sw.Close(); 16 } 17 } 18

以下のクラスは,ソリューションファイル右クリック→追加→新しい項目で作成しました。

C#

1 public class AirPortDataClass 2 { 3 public string ICAO_Code { get; set; } 4 public string Name { get; set; } 5 public string Latitude { get; set; } 6 public string Longitude { get; set; } 7 public string Azimuth { get; set; } 8 } 9

###補足情報(言語/FW/ツール等のバージョンなど)
言語はC#
開発環境はVisualStudio2017
.NET Framework Version 4.7.02556
です

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ShikaTech

2018/01/10 10:28 編集

回答者に8分超えの動画を全部見ろってのは酷だとは思いませんか?参考にした動画の全コードを転記、あるいは要点を説明してください。エラー自体はairPortDataClassBindingSourceがNULL、つまりSaveFileメソッドとは別の処理で何かしらの初期化なり読み込みなりが行なわれている前提なのに、そのあたりの何かがバグってるってところですかね。
P5_USER

2018/01/10 11:03

配慮が足りませんでした。再編集にて転記しました。ボタンクリックイベントで発生するSaveFileメソッド以外には何も入れていないのですが...どこかで悪さをしているところを探すしかないですか
guest

回答2

0

次のコードで実験をしてみました。

C#

1bindingSource1.DataSource = Enumerable.Range(0, 100); 2Debug.WriteLine($"Type: {bindingSource1.DataSource.GetType()}"); 3Debug.WriteLine($"IEnumerable<T>: {bindingSource1.DataSource is IEnumerable<int>}"); 4Debug.WriteLine($"IList<T>: {bindingSource1.DataSource is IList<int>}"); 5Debug.WriteLine($"List<T>: {bindingSource1.DataSource is List<int>}");

結果は次のようになりました。

C#

1Type: System.Linq.Enumerable+<RangeIterator>d__110 2IEnumerable<T>: True 3IList<T>: False 4List<T>: False

どういうことかと言うと、BindingSource.DataSourceIEnumerable<T> を入れても、それが自動的に List<T> になることはありません。

CsvHelper のドキュメント によると、GetRecords<T>() の戻り値は IEnumerable<T> となっています。
ですので、DataSource の中身は List<T> ではありません。

なぜ写経元のコードで動いているのかわかりませんが、もともと間違ったものを写経していた可能性もあります。

下記の部分

C#

1foreach (AirPortDataClass AirPortData in airPortDataClassBindingSource.DataSource as List<AirPortDataClass>)

これを

C#

1foreach (AirPortDataClass AirPortData in airPortDataClassBindingSource.DataSource as IEnumerable<AirPortDataClass>)

に変更してみてください。

また今回は動きませんでしたが、仮に List<T> に変換成功して動いたとします。
しかしそれはたまたま内部で List<T> に変換された結果で、フレームワークのバージョンアップとともにいつ動かなくなるかわからないものですから、そんなコードを書いてはいけません。
DataSource に入れたものと同じクラス・インターフェースでアクセスしましょう。

追記

もしかしたら DataSource = ...GetRecords().ToList() とすべきところを DataSource = GetRecords() としているのかも知れません。ToList() をつければ IEnumerable にしなくても動くと思います。

投稿2018/01/12 23:30

編集2018/01/12 23:40
Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

P5_USER

2018/01/13 01:44

Zuishinさん ありがとうございます。 変更してみますと,foreach内のinでエラーが発生してしまいました。 ObjectDisposedExceptionというタイトルで「閉じているTextReaderから読み取ることはできません」とありました。 であれば,とusingのStreamWriterをTextWriterに変更したところ,コードエラーで 「抽象クラスまたはインターフェイスTextWriterのインスタンスを作成できません。」 と出てしまいました。 CsvHelperの説明を読んで,CsvWriterの()内にStreamWriterを記述してusingしても, foreachのinで同じ文章が返ってきました。 TextWriterという抽象クラスの中にStreamWriterがクラスとして存在しているはずなのにStreamWriterがTextWriterとして受け入れてくれないのはなぜなのでしょうか。
Zuishin

2018/01/13 02:24

遅延読み取りに失敗しているんだと思います。DataSource に入れるときに GetRecords().ToList() してみてください。
P5_USER

2018/01/13 02:29

foreach (AirPortDataClass AirPortData in airPortDataClassBindingSource.DataSource as IEnumerable<AirPortDataClass>) の.DataSourceの後ろということでしょうか。 .を後ろに入れた際にインテリセンスが機能しますが,Equals/GetHashCode/GetType/Tostringの4つしか候補に挙がっていませんでした。
Zuishin

2018/01/13 02:30

いえ、foreach ではなく DataSource に入れているところです。 studentBindingSource.DataSource = csv.GetRecords<Student>(); これを studentBindingSource.DataSource = csv.GetRecords<Student>().ToList(); に変更してください。
P5_USER

2018/01/13 02:41

変更したところ問題なく動きました。 ただ,CSVファイルを見ても,何も書き込まれていない状態でした。 ファイルパスをフルパスで指定しているのにも関わらず,書き込まれていないのは何故なのでしょうか。
Zuishin

2018/01/13 02:44

一番上のソースは実際のものじゃないですよね? これにあたる部分を追記してください。
P5_USER

2018/01/13 02:48

私の,btnRead_Clickに当たるところを書き換えましたが,うまくいきませんでした。
Zuishin

2018/01/13 03:03

書き換えた後のものをお願いします。 どこをどう書き換えたのかわからなければわかるはずがありません。
P5_USER

2018/01/13 03:28

private void Read_And_ViewFile(string filePath) { // CSVファイルオープン using (OpenFileDialog ofd = new OpenFileDialog() { Filter = "csv|*.csv", ValidateNames = true }) { if (ofd.ShowDialog() == DialogResult.OK) { var sr = new StreamReader(new FileStream(ofd.FileName, FileMode.Open)); var csv = new CsvReader(sr); airPortDataClassBindingSource.DataSource = csv.GetRecords<AirPortDataClass>().ToList(); } } } 失礼しました。 if()の最後の行のところを指摘いただいたように,.ToList()と書き加えました。
Zuishin

2018/01/13 03:36

では ofd.FileName で示されたファイルの最初の数行と AirPortDataClass の定義をお願いします。 うまくマッピングできていないのかもしれません。
P5_USER

2018/01/13 03:44

AirPortDataClassは以下のようになっています。 namespace KMAP { public class AirPortDataClass { public string ICAO_Code { get; set; } public string Name { get; set; } public string Latitude { get; set; } public string Longitude { get; set; } public string Azimuth { get; set; } } ofd.FileName で示されたファイルの最初の数行 というのは読み込み/書き込みするCSVファイルの中の最初の行ということでしょうか?
Zuishin

2018/01/13 03:46

最初の行だけでは不十分です。五行くらいお願いします。
P5_USER

2018/01/13 03:55

理解が足らず申し訳ありません。 定義というのは, ①:ソースコード内でファイルに数行を書き込んで定義させる。 ②:ソースコード外でファイルに数行を書き込んで定義させる。(エクスプローラのファイルから直接CSVに書き込んでおく) のどちらでしょうか。
Zuishin

2018/01/13 06:17

定義の方は先ほど書いていただいたクラス定義のことを思っていましたのでこれで OK です。 もう一つは CSV の中身がどうなっているのかを知りたいので、ファイル内容を 5 行ほど書いてください。
Zuishin

2018/01/13 06:17

もちろん 5 行なければ全部書いてください。
P5_USER

2018/01/13 06:51

CSVファイルに直接 RJNA,県営名古屋空港,135,35,300 RJNK,小松空港,135,35,200 RJNF,福井空港,135,35,100 RJNT,富山空港,135,35,50 RJAA,成田空港,135,35,20 と入れてみました。 すると,Read_And_ViewFileメソッドのToList()の行で CsvHelper.ValidationException: 'Header matching ['ICAO_Code'] names at index 0 was not found. If you are expecting some headers to be missing and want to ignore this validation, set the configuration HeaderValidated to null. You can also change the functionality to do something else, like logging the issue.' と出てしまいました。
Zuishin

2018/01/13 06:53

コラム名がありません。最初の行にプロパティ名を入れてください。 ICAO_Code,Name,Latitude,Longitude,Azimuth
P5_USER

2018/01/13 06:58

読みこめました。 ただ,Nameに当たるところがトランプのダイヤマークに?が書かれた文字に置き換わっていて, 文字化けしていました。SHIFT-JISがいけないんでしょうか。
Zuishin

2018/01/13 07:01

そうですね。エンコーディングを指定して読み込むこともできますが、特別な事情が無い限り今はどのファイルも UTF-8 で作りましょう。
P5_USER

2018/01/13 07:11

SaveFileメソッドの引数を”SHIFT-JIS”から”UTF-8”に切り替えても,文字化けがなおりませんでした。 また,読み込みボタンでRead_And_ViewFileメソッドが呼び出されるのですが, データグリッドビューにコラム名しかない状態で表示されてしまいました。 CSVデータも,空になっていました...
Zuishin

2018/01/13 07:13

引数を UTF-8 に切り替えただけでファイル自体は SHIFT-JIS なのではありませんか?
P5_USER

2018/01/13 07:26

そうでした。 保存の時に,UTF-8にできたので切り替えて読み込んだら,日本語が読み込めました。 ただ,やっぱり 読み込みボタンクリック(CSV読み込み) ↓ データグリッドビュー表示(内容が反映されている) ↓ 保存ボタンクリック(CSV保存) ↓ 読み込みボタンクリック ↓ データグリッドビュー表示(CSVの中身が空っぽ) でした。 初期化されているのでしょうか...
Zuishin

2018/01/13 07:46

問題が変わってきているようですし、散らばりすぎて要点がわからなくなっています。 とりあえずデータ型が違うのではないかという Tak1wa さんの予想が大当たりだったということがわかったのでそちらにベストアンサーをつけ、データが消える問題に関しては全く別の問題なので新たに質問を立ててください。 その際には写経元のソースはどうでもいいので、読み込みボタンのイベントハンドラ、保存ボタンのイベントハンドラなど処理の流れを追えるよう省略せず書いてください。 この質問とは違う独立した質問なので、ここを全く見ていない人にも意味が通じるよう気をつけて書いてください。
P5_USER

2018/01/13 07:49

そうですね。 付き合っていただきありがとうございました。
guest

0

ベストアンサー

こんにちは。

airPortDataClassBindingSource.DataSource はnull ですか?
null でないのであれば、データ型はなんですか?
List<AirPortDataClass> に変換できる型でなければここでnullになります。
studentのままになってたりしませんか。

投稿2018/01/10 13:23

Tak1wa

総合スコア4791

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

P5_USER

2018/01/10 13:58

Tak1waさん エラーを吐いたところで変数の中身を見てみますと,((System.Collections.Generic.IEnumerator<KMAP.AirPortDataClass>)airPortDataClassBindingSource.DataSource).Current がnullと表示されていました。 studentとAirPortDataClassは同じのソリューションファイルに存在していないので,今回のエラーを吐いたファイルには存在していません。
Tak1wa

2018/01/10 15:14

>studentとAirPortDataClassは同じのソリューションファイルに存在していないので,今回のエラーを吐いたファイルには存在していません これの意味がわかりません。AirPortDataClassもstudentも存在しないといいたいのでしょうか。エラーの吐いた「ファイル」に存在するのかどうかは関係ないと思います。別のクラスですよね? >((System.Collections.Generic.IEnumerator<KMAP.AirPortDataClass>)airPortDataClassBindingSource.DataSource).Current これは何で確認したのですかね、CurrentがNullなのが確認できたのであれば、Null参照例外が発生していないということなので、((System.Collections.Generic.IEnumerator<KMAP.AirPortDataClass>)airPortDataClassBindingSource.DataSource)はNullではないですよね。 遅延実行絡みかもしれないので、 `((System.Collections.Generic.IEnumerator<KMAP.AirPortDataClass>)airPortDataClassBindingSource.DataSource).ToList()`のデバッグ結果どうなりますかね
P5_USER

2018/01/11 01:30

>別のクラスですよね? 別のクラスになっています。 >これは何で確認したのですかね, SaveFileメソッドがボタンクリックで実行されるようになっているのですが,該当するボタンをクリックした際に,ハンドルされていない例外として実行が中断された際にカーソルを行に合わせて確認しました。 >ToList()`のデバッグ結果どうなりますかね foreachの1つ上の行にコードを書いて実行してみましたが,ToList()がインテリセンスの候補に入っていませんでした。
Tak1wa

2018/01/11 12:08

すみません、勘違いしてました。 CurrentがNullになる場合をどうにかしたいのであれば、foreach直後にnullチェックするか、LINQなどでNULLの要素を除外するようにしてはどうでしょうか。
P5_USER

2018/01/12 02:16

ということは,foreachの直下の行にLINQというものを使ってairPortDataClassBindingSource.DataSourceから NULLの入っているデータを除外するコードを追加する。ということでしょうか。
Tak1wa

2018/01/12 04:59

「LINQというもの」とのことなのでLINQのことは忘れて、foreach直後にnullチェックしてみてください。
P5_USER

2018/01/12 05:24

https://ameblo.jp/i-devdev-beginner/entry-12292363493.html こちらの方のを参考にしていますが, foreach (var item in list.OrEmptyIfNull()) のlistがList<string>になっていました。 現在,私のものはforeach (AirPortDataClass AirPortData in airPortDataClassBindingSource.DataSource as List<AirPortDataClass>) となっています。 早速,クラスを作成したのですが,Datasourceの後ろにもList<AirPortDataClass>の後ろにも インテリセンスが反応しませんでした。
Tak1wa

2018/01/12 06:50

これはコレクションがNULLかどうかをチェックする拡張メソッドです。 要素をチェックするものではないですし、拡張メソッドなので自分で作成する必要があると思います。 foreachの行で本当に例外が起きてるのか確認してください。 foreachの次行に `if(AirPortData == null) continue;` とかやったら動いたりしないですかね。
P5_USER

2018/01/12 07:17

なるほど。 教えていただいたコードを追記して実行すると, ’オブジェクト参照がオブジェクトインスタンスに設定されていません’ と表示されてしまいました。
Tak1wa

2018/01/12 07:53

デバッグ画面のキャプチャありますか?
P5_USER

2018/01/12 07:59

質問文に追加しました。 このようになっています。
Tak1wa

2018/01/12 08:05

ありがとうございます。airPortDataClassBindingSourceをウォッチできますか?
P5_USER

2018/01/12 08:15

同じく質問文に追加しました。
Tak1wa

2018/01/12 08:24

airPortDataClassBindingSource.DataSource のウォッチをお願いします。 as での型変換が出来なくてNullになってるんだと思いますが、なぜ失敗しているのだろう。
P5_USER

2018/01/12 08:38

少し見えにくいですが,このようになっています。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問