13

適切な例外処理に関する多くのブログ/記事/本の章を読みましたが、それでもこのトピックは私には明確ではありません。次の例で私の質問を説明しようとします。

次の要件を持つクラス メソッドを考えてみましょう。

  1. ファイルパスのリストをパラメータとして受け取る
  2. 各ファイルのファイルコンテンツを読み取るか、それをしようとして問題がある場合はスキップします
  3. ファイルの内容を表すオブジェクトのリストを返す

したがって、仕様は簡単で、コーディングを開始する方法は次のとおりです。

    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;
    }

適切な例外処理の次の規則は次のとおりです。処理できる特定の例外のみをキャッチします。スローされる可能性のある特定の例外の処理については気にしません。ファイルを読み取れるかどうかを確認したいだけです。どうすれば適切なベストプラクティスの方法でそれを行うことができますか?

4

7 に答える 7

5

あなたの要件は明確です - 読めないファイルをスキップしてください。では、一般的な例外ハンドラの問題は何でしょうか? これにより、簡単、クリーン、読みやすく、スケーラブルで保守可能な方法でタスクを実行できます。

将来、可能性のある複数の例外を別の方法で処理したい場合は、一般的な例外の上に特定の例外のキャッチを追加するだけです。

それでは、以下のコードを見たいですか?ファイルの読み取りを処理するコードをさらに追加する場合は、このリストに新しい例外を追加する必要があることに注意してください。これはすべて何もしないのですか?

try
{
    // find, open, read files
}
catch(FileNotFoundException) { }
catch(AccessViolation) { }
catch(...) { }
catch(...) { }
catch(...) { }
catch(...) { }
catch(...) { }
catch(...) { }

規則はガイドラインであり、優れたコードを作成するために遵守しようとするのに最適ですが、適切なエチケットの奇妙な感覚を維持するためだけにコードを過度に複雑にしないでください。

私にとって、適切なエチケットは、トイレで話さないことです。しかし、そこで上司があなたに挨拶すると、あなたは挨拶を返します。したがって、複数の例外を異なる方法で処理したくない場合は、それぞれをキャッチする必要はありません。


編集:だから私は以下をお勧めします

try
{
    // find, open, read files
}
catch { } // Ignore any and all exceptions

上記は、どの例外がスローされたかを気にしないように指示しています。System.Exception だけであっても、例外を指定しないことで、.NET が既定値になるようにしました。したがって、以下はまったく同じコードです。

try
{
    // find, open, read files
}
catch(Exception) { } // Ignore any and all exceptions

または、少なくともログに記録する場合:

try
{
    // find, open, read files
}
catch(Exception ex) { Logger.Log(ex); }  // Log any and all exceptions
于 2013-11-07T14:58:36.640 に答える
2

1 つのメソッドにさまざまなアクションを混在させています。コードを変更すると、質問しやすくなります。

static List<FileContent> GetFileContents(List<string> paths)
{
    var resultList = new List<FileContent>();

    foreach (var path in paths)
    {
          if (CanReadFile(path){
                resultList.Add(new FileContent(path, buffer));
          }
    return resultList;
}

static bool CanReadFile(string Path){
     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);
            }
     }catch(Exception){ //I do not care what when wrong, error when reading from file
         return false;
     }
     return true;
}

このようにして、CanReadFile はチェックの実装を隠します。考えなければならないことは、CanReadFile メソッドが正しいかどうか、またはエラー処理が必要かどうかだけです。

于 2013-11-07T15:06:00.300 に答える
2

この質問に対する私の解決策は、通常、考えられる例外の数に基づいています。数が少ない場合は、それぞれに catch ブロックを指定します。可能性のある例外が多数ある場合は、すべての例外をキャッチします。開発者に特定の例外を常にキャッチするように強制すると、非常に醜いコードが作成される可能性があります。

于 2013-11-07T14:51:15.587 に答える
2

この例で考えられるのは、FileNotFoundExceptionが多すぎてキャッチできない と、最も一般的なExceptionの間にまだレイヤーがあるということですIOException

一般に、例外を可能な限り具体的にキャッチしようとしますが、実際にエラーをスローするために例外を使用せずに例外をキャッチする場合は特に、例外のグループをキャッチすることもできます。それでも、できるだけ具体的にしようとします

于 2013-11-07T14:59:26.450 に答える