LoginSignup
3
2

More than 3 years have passed since last update.

【C#, log4net】シングルトンクラスのコンストラクタでロガーを使ったらNullReferenceExceptionが発生して苦闘した話

Last updated at Posted at 2019-12-15

追記 2019/12/15

コメント欄にてalbireoさんにシングルトンの実装の仕方などについてご指摘いただきましたので、
そちらも一緒にお読みいただければと思います。

はじめに

シングルトンクラスを作り、そのインスタンスを外部のクラスから取得しようとしたら、下記の画像のような例外が発生した。
原因はシングルトンクラスに書いていたlog4netのロガーインスタンスを取得するコード(GetLoggerメソッド)にあった。
色々すっ飛ばしてこの話の結論を先に述べると、「ロガーインスタンス取得コードはフィールドの一番最初に書きましょう」。
もし同じ問題ではまっている方がいればとの思いで記事を書いた(いるかな・・・)。
※シングルトンパターンについて正しくない説明があるかもしれませんので、その点はご了承いただいたうえでお読みください。
image.png

環境

・IDEはVisual Studio 2017を使用

問題の発生まで

このサイト(.NET TIPS:シングルトンパターンを実現するには?[C#/VB])を参考にして、シングルトンクラスを作成し、ログ出力するためロガーインスタンス取得コードを書いた。

下記がそのシングルトンクラス。

SingletonClass.cs
namespace Hoge
{
    class SingletonClass
    {
        // 自身のクラスのインスタンスを初期化(アプリ実行時にインスタンスはこの1つしか存在しない)
        private static SingletonClass _instance = new SingletonClass();

        // ロガーインスタンス取得コード
        private static readonly log4net.ILog _logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        // 自身のインスタンスを外部から使うための手段を用意してあげる
        // プロパティを使った方法
        public static SingletonClass Instance => _instance;
        // メソッドを使った方法
        public static SingletonClass GetInstance() => _instance;


        // 外部から直接呼べないコンストラクタ
        private SingletonClass()
        {
            _logger.Debug("SingletonClassのインスタンスが生成されました");
        }
    }
}

 
そして、シングルトンクラスを利用する側のクラスで、シングルトンインスタンス取得コードを書いた。

UseSingleton.cs
namespace Hoge
{
    class UseSingleton
    {
        // シングルトンインスタンスを保持するフィールド
        private SingletonClass _singletonInstance;

        public void SomeMethod()
        {
            // シングルトンインスタンスを取得
            _singletonInstance = SingletonClass.GetInstance();         
        }
    }
} 

 

実行したら、落ちた。
image.png

問題の原因

問題のコードは下図の矢印の順序で処理が行われている。
まずGetInstanceメソッドを呼び出すと、シングルトンクラス内部ではシングルトンインスタンスを取得しようとする。
その際にシングルトンインスタンスがnewされ、したがってシングルトンクラスのコンストラクタに処理が移る。
ここからが問題。
今回はコンストラクタでロガーを使ったログ出力を行おうとした。
しかしこの時点でロガーインスタンスの取得は実行されていないため_loggerはNullの状態だったのだ。
まだ取得してないはずの_loggerを使おうとしたから、「オブジェクト参照がオブジェクトインスタンスに設定されていません」と怒られたのであった・・・。

image.png

解決

シングルトンクラスにおいて、ロガーインスタンス取得コードを自身のインスタンス初期化コードより先に書いてあげ、無事に動いた。

SingletonClass.cs

    class SingletonClass
    {
        // ロガーインスタンス取得コード
        private static readonly log4net.ILog _logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        // 自身のクラスのインスタンスを初期化
        private static SingletonClass _instance = new SingletonClass();


3
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2