コンストラクタでの例外はあり?なし?


コンストラクタでの例外はあり?なし?
http://togetter.com/li/54761


私の昨日の記事が発端でなんか興味深い議論になっている模様。


以下、twitterでは文字数的に書ききれないことをだらだらと。


二段階初期化(コンストラクタとは別に初期化関数を呼んでオブジェクトの構築をするような設計)は面倒なのでC#では私は、やらないですね。それで困ったことは特に無いのでまあ問題ないんだろうとは思ってます。また、コンストラクタでオブジェクトの構築が終わっていて、そのあとはimmutable(=内部の状態が不変)であるほうがオブジェクトの性質として望ましいというのもあります。


ああ、あと巨大なresourceを扱うなら、管理用のクラス(cache sizeの上限を設けて、アクセスしたtime stampの古いresourceから自動的に解放するだとか)を作るでしょうから、try〜catch忘れたためにリソースリークという憂き目には遭ったことは一度も無いです。


まあ、それとは別に、現実的に言って、C#では .NET Frameworkがコンストラクタで例外投げてくるので、その流儀に合わせたほうがいいと考えています。例えば次のようなfactoryを書いた場合、どうなるでしょうか。

StreamReader StreamReaderFactory(bool isCustomed , string path)
{
  return isCustomed ? new MyStreamReader(path) : new StreamReader(path);
}

MyStreamReaderはStreamReaderの派生クラスだとします。StreamReaderはコンストラクタで例外を投げます。もし、MyStreamReaderがコンストラクタで例外を投げないように設計していたらどうでしょうか。↑のプログラムのnew StreamReaderの部分を、コンストラクタでの例外を封じ込めるようにtry〜catchをこのStreamReaderFactoryのなかで書かないとMyStreamReaderとの整合性が取れません。


このようにコンストラクタで例外を投げてくるようなフレームワークと仲良くやっていこうと思うと自分もその流儀に従わないと、思わぬところにtry〜catchが必要になります。


例えば「例外を投げるのはもう死んでも嫌」という人が、例外をぽんぽん投げてくるフレームワークを使おうとしたら、そのフレームワークAPIを呼び出すごとにtry〜catchが必要になります。場合によっては、例外を一切出さないNoThrowStreamReaderみたいなwrapper classを片っ端から作るハメになります。.NET Frameworkのような広大なライブラリをすべてwrapしていくのは馬鹿げています。また、そのようにしたからと言って優れたコードが書けるわけでも、とりわけバグが減るわけでもありません。


一番簡単なソリューションはそのフレームワークを受け入れることです。「このフレームワークは例外ぽんぽん投げてきやがるな…大丈夫だ。問題ない。(by エルシャダイ)」と思うことです。自分もそのスタイルで書いてみることです。そうして初めてフレームワークからの恩恵が受けられるのです。


…と他人のフレームワークを使うのが大嫌いでゲームライブラリを子供のころから何度も何度も作りなおしている私が言ってみる。