適切な例外処理に関する多くのブログ/記事/本の章を読みましたが、それでもこのトピックは私には明確ではありません。次の例で私の質問を説明しようとします。
次の要件を持つクラス メソッドを考えてみましょう。
- ファイルパスのリストをパラメータとして受け取る
- 各ファイルのファイルコンテンツを読み取るか、それをしようとして問題がある場合はスキップします
- ファイルの内容を表すオブジェクトのリストを返す
したがって、仕様は簡単で、コーディングを開始する方法は次のとおりです。
public class FileContent
{
public string FilePath { get; set; }
public byte[] Content { get; set; }
public FileContent(string filePath, byte[] content)
{
this.FilePath = filePath;
this.Content = content;
}
}
static List<FileContent> GetFileContents(List<string> paths)
{
var resultList = new List<FileContent>();
foreach (var path in paths)
{
// open file pointed by "path"
// read file to FileContent object
// add FileContent to resultList
// close file
}
return resultList;
}
ここで、仕様の 2. は、メソッドが「何らかの理由でコンテンツを読み取れないファイルをスキップする」必要があることを示していることに注意してください。したがって、これにはさまざまな理由が考えられます (たとえば、ファイルが存在しない、セキュリティ権限がないためにファイル アクセスが拒否された、ファイルがロックされていて他のアプリケーションで使用されているなど)。理由が何であるかは気にしません。可能であればファイルの内容を読み取り、そうでない場合はファイルをスキップしたいだけです。エラーが何であるかは気にしません...
では、このメソッドを適切に実装するにはどうすればよいでしょうか。
OK 適切な例外処理の最初のルールは、一般的な例外を決してキャッチしないことです。したがって、このコードは適切ではありません。
static List<FileContent> GetFileContents(List<string> paths)
{
var resultList = new List<FileContent>();
foreach (var path in paths)
{
try
{
using (FileStream stream = File.Open(path, FileMode.Open))
using (BinaryReader reader = new BinaryReader(stream))
{
int fileLength = (int)stream.Length;
byte[] buffer = new byte[fileLength];
reader.Read(buffer, 0, fileLength);
resultList.Add(new FileContent(path, buffer));
}
}
catch (Exception ex)
{
// this file can't be read, do nothing... just skip the file
}
}
return resultList;
}
適切な例外処理の次の規則は次のとおりです。処理できる特定の例外のみをキャッチします。スローされる可能性のある特定の例外の処理については気にしません。ファイルを読み取れるかどうかを確認したいだけです。どうすれば適切なベストプラクティスの方法でそれを行うことができますか?