よく、ファイルの読み込み処理を実装することがあります。
まぁ、読み込み処理だけに限ったものではないのですが、
・どこまで、例外を考慮して実装するか
・例外が発生した場合の実装はどうするか
などがあいまいになってしまい、とりあえず正常系だけ実装してしまうことがありました。
そういった場面でも
「とりあえずこれだけ実装しておけば何とかなるよ!」
という、指針になる実装は何かないかと考えていました。
・・・そんな時、偉大な先生であるlog4netでそういった実装が見つかったので、詳しく見ていきたいと思います。
場所は、XmlConfiguratorクラスのInternalConfigureメソッドです。
特に参考にしたい部分を抜粋してみました。
なお、configFile変数は、FileInfo型です。
if (File.Exists(configFile.FullName)) { // Open the file for reading FileStream fs = null; // Try hard to open the file for(int retry = 5; --retry >= 0; ) { try { fs = configFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read); break; } catch(IOException ex) { if (retry == 0) { LogLog.Error(declaringType, "Failed to open XML config file [" + configFile.Name + "]", ex); // The stream cannot be valid fs = null; } System.Threading.Thread.Sleep(250); } } if (fs != null) { try { // Load the configuration from the stream InternalConfigure(repository, fs); } finally { // Force the file closed whatever happens fs.Close(); } } } else { LogLog.Debug(declaringType, "config file [" + configFile.FullName + "] not found. Configuration unchanged."); }
ここでは、ファイルの存在を確認。
ファイルがあればFileStreamクラスのインスタンスを生成。
ファイルの読み込みを実行する。
という大きな流れで処理を進めています。
・面白い、勉強になった点
ファイルの存在確認もちゃんとやらないといけないことがよくわかりました。
特徴的なのは、5回リトライ処理が実装されているところです。
1回ごとに、threadを250ミリ秒停止するのも特徴的です。
こういう実装をするとき、失敗したらとりあえずメッセージに出す?とかしか考えてこなかったので、こういう処理はとても新鮮でした!
・気になった点
close処理をFinallyで実施しているとはいえ、IDisposeを実装しているクラスを利用するときは、やっぱりusing句を利用したほうが良いのではないかと思いました。
また、thread.Sleepより、Timerクラスとかを利用して、250ミリ秒間ループ処理させるのでもありなのではないかと思いました。
thread停止して原因が解決されるのであればよいと思うのですが・・・
以上を踏まえて、自分なりのリファレンス実装を残しておきます!
String filePath = ""; if (File.Exists(filePath)) { try { using (FileStream fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { //読み込み成功したときの処理 } } catch (Exception) { //ファイルが読み込みに失敗したときの処理 } } else { //ファイルが存在しないときの処理 }
やってみて気が付いたのですが、usingで宣言した変数には、その後の代入はできません。
そういう理由のために、log4netでも、独自でFinally句にClose処理を実装していたらしいです。
まとめ
あさはかでした・・・
より良い実装があったら更新していきます(´・ω・`)