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

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

ただいまの
回答率

91.24%

  • C#

    5018questions

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

  • Visual Studio

    1276questions

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

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

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 244

P5_USER

score 17

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

以下の参考にしたものに書いてあります動画を元に写経をしてアレンジしました。
実行すると,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 を返しました。


イメージ説明
イメージ説明
イメージ説明

該当のソースコード

        /// <summary>
        /// CSVのファイルパスを指定して保存する
        /// </summary>
        /// <param name="filePath">CSVのファイルパスを指定します</param>
        private void SaveFile(string filePath)
        {        
            using (var sw = new StreamWriter(filePath, false, Encoding.GetEncoding("SHIFT-JIS")))
            {
                var writer = new CsvWriter(sw);
                writer.WriteHeader(typeof(AirPortDataClass));
                foreach (AirPortDataClass AirPortData in airPortDataClassBindingSource.DataSource as List<AirPortDataClass>)
                {
                    writer.WriteRecord(AirPortData);
                }
                sw.Close();
            }
        }


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

    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; }
    }

補足情報(言語/FW/ツール等のバージョンなど)

言語はC#
開発環境はVisualStudio2017
.NET Framework Version 4.7.02556
です

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • ShikaTech

    2018/01/10 19:28 編集

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

    キャンセル

  • P5_USER

    2018/01/10 20:03

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

    キャンセル

回答 2

checkベストアンサー

+1

こんにちは。

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/10 22:58

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

    キャンセル

  • 2018/01/11 00: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()`のデバッグ結果どうなりますかね

    キャンセル

  • 2018/01/11 10:30

    >別のクラスですよね?
    別のクラスになっています。

    >これは何で確認したのですかね,
    SaveFileメソッドがボタンクリックで実行されるようになっているのですが,該当するボタンをクリックした際に,ハンドルされていない例外として実行が中断された際にカーソルを行に合わせて確認しました。

    >ToList()`のデバッグ結果どうなりますかね
    foreachの1つ上の行にコードを書いて実行してみましたが,ToList()がインテリセンスの候補に入っていませんでした。

    キャンセル

  • 2018/01/11 21:08

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

    キャンセル

  • 2018/01/12 11:16

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

    キャンセル

  • 2018/01/12 13:59

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

    キャンセル

  • 2018/01/12 14: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>の後ろにも
    インテリセンスが反応しませんでした。

    キャンセル

  • 2018/01/12 15:50

    これはコレクションがNULLかどうかをチェックする拡張メソッドです。
    要素をチェックするものではないですし、拡張メソッドなので自分で作成する必要があると思います。

    foreachの行で本当に例外が起きてるのか確認してください。
    foreachの次行に
    `if(AirPortData == null) continue;` とかやったら動いたりしないですかね。

    キャンセル

  • 2018/01/12 16:17

    なるほど。

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

    キャンセル

  • 2018/01/12 16:53

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

    キャンセル

  • 2018/01/12 16:59

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

    キャンセル

  • 2018/01/12 17:05

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

    キャンセル

  • 2018/01/12 17:15

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

    キャンセル

  • 2018/01/12 17:24

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

    キャンセル

  • 2018/01/12 17:38

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

    キャンセル

+1

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

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

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

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

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

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

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

下記の部分

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

これを

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

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

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

追記

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/13 10:44

    Zuishinさん
    ありがとうございます。

    変更してみますと,foreach内のinでエラーが発生してしまいました。
    ObjectDisposedExceptionというタイトルで「閉じているTextReaderから読み取ることはできません」とありました。

    であれば,とusingのStreamWriterをTextWriterに変更したところ,コードエラーで
    「抽象クラスまたはインターフェイスTextWriterのインスタンスを作成できません。」
    と出てしまいました。

    CsvHelperの説明を読んで,CsvWriterの()内にStreamWriterを記述してusingしても,
    foreachのinで同じ文章が返ってきました。

    TextWriterという抽象クラスの中にStreamWriterがクラスとして存在しているはずなのにStreamWriterがTextWriterとして受け入れてくれないのはなぜなのでしょうか。

    キャンセル

  • 2018/01/13 11:24

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

    キャンセル

  • 2018/01/13 11:29

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

    キャンセル

  • 2018/01/13 11:30

    いえ、foreach ではなく DataSource に入れているところです。

    studentBindingSource.DataSource = csv.GetRecords<Student>();

    これを

    studentBindingSource.DataSource = csv.GetRecords<Student>().ToList();

    に変更してください。

    キャンセル

  • 2018/01/13 11:41

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

    キャンセル

  • 2018/01/13 11:44

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

    キャンセル

  • 2018/01/13 11:48

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

    キャンセル

  • 2018/01/13 12:03

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

    キャンセル

  • 2018/01/13 12: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()と書き加えました。

    キャンセル

  • 2018/01/13 12:36

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

    キャンセル

  • 2018/01/13 12: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ファイルの中の最初の行ということでしょうか?

    キャンセル

  • 2018/01/13 12:46

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

    キャンセル

  • 2018/01/13 12:55

    理解が足らず申し訳ありません。

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

    キャンセル

  • 2018/01/13 15:17

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

    キャンセル

  • 2018/01/13 15:17

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

    キャンセル

  • 2018/01/13 15: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.'
    と出てしまいました。

    キャンセル

  • 2018/01/13 15:53

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

    ICAO_Code,Name,Latitude,Longitude,Azimuth

    キャンセル

  • 2018/01/13 15:58

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

    キャンセル

  • 2018/01/13 16:01

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

    キャンセル

  • 2018/01/13 16:11

    SaveFileメソッドの引数を”SHIFT-JIS”から”UTF-8”に切り替えても,文字化けがなおりませんでした。

    また,読み込みボタンでRead_And_ViewFileメソッドが呼び出されるのですが,
    データグリッドビューにコラム名しかない状態で表示されてしまいました。
    CSVデータも,空になっていました...

    キャンセル

  • 2018/01/13 16:13

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

    キャンセル

  • 2018/01/13 16:26

    そうでした。
    保存の時に,UTF-8にできたので切り替えて読み込んだら,日本語が読み込めました。

    ただ,やっぱり
    読み込みボタンクリック(CSV読み込み)

    データグリッドビュー表示(内容が反映されている)

    保存ボタンクリック(CSV保存)

    読み込みボタンクリック

    データグリッドビュー表示(CSVの中身が空っぽ)
    でした。

    初期化されているのでしょうか...

    キャンセル

  • 2018/01/13 16:46

    問題が変わってきているようですし、散らばりすぎて要点がわからなくなっています。

    とりあえずデータ型が違うのではないかという Tak1wa さんの予想が大当たりだったということがわかったのでそちらにベストアンサーをつけ、データが消える問題に関しては全く別の問題なので新たに質問を立ててください。

    その際には写経元のソースはどうでもいいので、読み込みボタンのイベントハンドラ、保存ボタンのイベントハンドラなど処理の流れを追えるよう省略せず書いてください。

    この質問とは違う独立した質問なので、ここを全く見ていない人にも意味が通じるよう気をつけて書いてください。

    キャンセル

  • 2018/01/13 16:49

    そうですね。

    付き合っていただきありがとうございました。

    キャンセル

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

ただいまの回答率

91.24%

関連した質問

  • 解決済

    C# CSVファイルを配列に格納し、配列をグローバル変数として、別クラスで、使用したい。

    以下のコードで、CSVファイルを配列に格納して、 別クラスで、配列を使用したいのですが、 どのようにすればよいのでしょうか? c#歴1か月の初心者ですので、○○を参照とい

  • 解決済

    ファイルの書き出し

    前提・実現したいこと csvファイルから読み込んだものをソートして別のcsvファイルに書き出しを行いたいです。 発生している問題・エラーメッセージ 書き出しが行われない。

  • 解決済

    C# DatagridViewの使い方

    C# DataGridViewについて 1列目の1行目にA、2行目にB、3行目にCと表示したくて下記のようにコーディングしました。 namespace WindowsF

  • 解決済

    c# html~情報取得~表示

    私は、プログラミング言語のc#を学び始めた初心者です。 関東でIT関連の仕事をしています。 以前に、少し趣味でJavaを少しかじってましたが、自主的に少しやっていた程度です

  • 解決済

    C#のListの使い方について

    c#でプログラムを作成しているのですが Listの使い方がわからず使用方法が正しいのかがわかりません。 下記コードではList<dataModel>としdata1~data3の値は

  • 受付中

    UWPで2枚のImageを重ねて表示したいです

    こんにちは。  Windows10でUWPのアプリケーションを開発しています。  Visual Studio 2017 Communityを使っています。  前提・実現したいこ

  • 解決済

    Datetime型のリストに入った時刻の合計の出し方

    C#、VisualStudio初心者です。 Datetime型のリストに格納された時刻をすべて足したいのですが、 Timespanとどう合わせて計算すればいいのかわかりません。

  • 解決済

    Texture2Dをななめに切り出す方法

    Texture2Dをななめに切り出す方法を探しています。 4点を基準に画像を切り取る関数などはないでしょうか? GetPixelsの引数も一つの座標と幅、高さしかないためどう斜めに

同じタグがついた質問を見る

  • C#

    5018questions

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

  • Visual Studio

    1276questions

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