よく、C++でコンストラクタから例外を投げてはいけない(というか、投げる場合注意を払う必要がある)といいますが、C#はコンストラクタ内で例外を投げてもい良いのでしょうか?
まず、確認のため、以下のコードを実行します。
// 確認対象のクラス public class Sample : IDisposable { public Sample() { Console.WriteLine("コンストラクターが呼ばれました。"); throw new Exception(); // コンストラクター内で例外を発生させる } ~Sample() { Console.WriteLine("ファイナライザーが呼ばれました。"); } public void Dispose() { Console.WriteLine("Disposeが呼ばれました。"); } } // 上記クラスを実行する側 public static void Main(string[] args) { try { using (var sample = new Sample()) { } } catch (Exception ex) { Console.WriteLine(ex.ToString()); } GC.Collect(); Console.ReadLine(); // 出力内容 // > コンストラクターが呼ばれました。 // > System.Exception: 種類 'System.Exception' の例外がスローされました。 // > ファイナライザーが呼ばれました。 }
確認結果は以下の通りです。
- ファイナライザーがGCにより呼び出される。
- Disposeメソッドは呼び出されない。
つまり(適切にファイナライザーを記述すれば)「コンストラクターは例外を発生させても問題ない」です。以下のようなコードも全く問題ありません。
public class Sample : IDisposable { public Sample(string str) { // 入力値チェック異常を例外として応答する if(str == null) throw ArgumentNullException(nameof(str)); Console.WriteLine("コンストラクターが呼ばれました。"); throw new Exception(); // コンストラクター内で例外を発生させる }
但しコンストラクター内の処理で、オブジェクトの初期化以外の処理「アンマネージリソースの確保」や「初期化以外の処理」を記述するのは良くありません。そういった処理はコンストラクターではなく、メソッドで処理するようにします。
また、デストラクターでは通常、例外が発生しないように処理を記述するように注意する必要があります。
この性質を利用してDisposeパターンというものがあるので、デストラクターで処理が必要な場合そちらを使用するようにします。