基本的に、実際にファイルを開こうとする前に、ファイルを開く権限があるかどうかを確認したいと思います。必要がない限り、このチェックに try/catch を使用したくありません。事前に確認できるファイル アクセス プロパティはありますか?
6 に答える
私は過去に数え切れないほどこれを行ってきましたが、ほとんどの場合、その試みを行うことさえ間違っていました。
ファイルのパーミッション (ファイルの存在も含む) は揮発性であり、いつでも変更できます。マーフィーの法則のおかげで、これには特に、ファイルをチェックしてから開こうとするまでの短い期間が含まれます。ロックやネットワークの可用性、パスの解決など、これが失敗する理由は他にもあります。最初に確認する必要があることがわかっている場所にいる場合は、誤った結果になる可能性がさらに高くなります。しかし、奇妙なことに、かなり静的になりがちなテスト環境や開発環境では、この問題が発生することはありません。これにより、後で問題を追跡することが難しくなり、この種のバグが本番環境に移行しやすくなります。
これが意味することは、チェックにもかかわらず、ファイルのアクセス許可または存在が悪い場合でも、例外を処理する準備ができている必要があるということです。事前にファイルをチェックするかどうかに関係なく、例外処理コードが必要であり、優れた例外ハンドラーは、存在チェックまたはアクセス許可チェックのすべての機能を提供できます。
しかし、例外処理は遅くありませんか? よろしくお願いします。はい、そうです。実際、スタックを巻き戻して例外を処理することは、1 台のコンピューター内で実行できる最も遅い処理です。ただし、ディスク I/O はさらに低速であり、関数の呼び出しやアクセス許可のチェックを行うと、常にファイル システムで追加の I/O 操作が強制されることを覚えておくことが重要です。.Exists()
したがって、ファイルを開こうとする前の最初のチェックは冗長で無駄が多いことがわかります。例外処理に勝る利点はありません。それは実際にあなたのパフォーマンスを助けるのではなく、傷つけます. 維持しなければならないコードが増えるという点で、コストが追加されます。最後に、微妙なバグが発生する可能性があります。初期チェックを行うメリットはまったくありません。
代わりに、ここでの正しいことは、最初のチェックなしですぐにファイルを開こうとし、失敗した場合は適切な例外ハンドラーに努力を注ぐことです。アクセス許可、ロック、またはファイルが存在するかどうかを確認する場合でも、同じことが当てはまります。
要約すると、コードを増やして毎回ファイル チェックに余分なコストを支払うか、コードを減らして一部の時間だけ例外処理に小さいながらも悪いコストを支払うかの選択です。
同様の問題でここに来る他の人のための簡単なヒント:
DropBoxなどのWeb同期アプリに注意してください。.NETで「using」ステートメント(Disposeパターン)が壊れていると思って2時間過ごしました。
私は最終的に、Dropboxがファイルを同期するために、バックグラウンドでファイルの読み取りと書き込みを継続的に行っていることに気付きました。
Visual Studio Projectsフォルダーがどこにあるかを推測しますか?もちろん、「MyDropbox」フォルダ内。
したがって、アプリケーションをデバッグモードで実行すると、アプリケーションが読み取りおよび書き込みを行っていたファイルにも、DropBoxサーバーと同期するためにDropBoxから継続的にアクセスされていました。これにより、ロック/アクセスの競合が発生しました。
したがって、少なくとも、より堅牢なファイルオープン関数(つまり、複数回試行するTryOpen())が必要であることがわかりました。フレームワークの組み込み部分ではないことに驚いています。
[アップデート]
これが私のヘルパー関数です:
/// <summary>
/// Tries to open a file, with a user defined number of attempt and Sleep delay between attempts.
/// </summary>
/// <param name="filePath">The full file path to be opened</param>
/// <param name="fileMode">Required file mode enum value(see MSDN documentation)</param>
/// <param name="fileAccess">Required file access enum value(see MSDN documentation)</param>
/// <param name="fileShare">Required file share enum value(see MSDN documentation)</param>
/// <param name="maximumAttempts">The total number of attempts to make (multiply by attemptWaitMS for the maximum time the function with Try opening the file)</param>
/// <param name="attemptWaitMS">The delay in Milliseconds between each attempt.</param>
/// <returns>A valid FileStream object for the opened file, or null if the File could not be opened after the required attempts</returns>
public FileStream TryOpen(string filePath, FileMode fileMode, FileAccess fileAccess,FileShare fileShare,int maximumAttempts,int attemptWaitMS)
{
FileStream fs = null;
int attempts = 0;
// Loop allow multiple attempts
while (true)
{
try
{
fs = File.Open(filePath, fileMode, fileAccess, fileShare);
//If we get here, the File.Open succeeded, so break out of the loop and return the FileStream
break;
}
catch (IOException ioEx)
{
// IOExcception is thrown if the file is in use by another process.
// Check the numbere of attempts to ensure no infinite loop
attempts++;
if (attempts > maximumAttempts)
{
// Too many attempts,cannot Open File, break and return null
fs = null;
break;
}
else
{
// Sleep before making another attempt
Thread.Sleep(attemptWaitMS);
}
}
}
// Reutn the filestream, may be valid or null
return fs;
}
まず、ジョエル・コーホーンが言ったこと。
また、必要な場合を除き、try/catch の使用を避けたいという願望の根底にある仮定を調べる必要があります。例外に依存するロジックを回避する一般的な理由 (Exception
オブジェクトの作成のパフォーマンスが低下する) は、おそらくファイルを開くコードには関係ありません。
ディレクトリサブツリー内のすべてのファイルを開いてデータを入力するメソッドを作成していてList<FileStream>
、それらの多くにアクセスできないことが予想される場合は、ファイルを開こうとする前にファイルのアクセス許可を確認して、アクセスできないようにすることをお勧めします。例外が多すぎます。しかし、それでも例外を処理します。また、これを行うメソッドを作成している場合は、プログラムの設計に何かひどく問題がある可能性があります。
public static FileStream GetFileStream(String filePath, FileMode fileMode, FileAccess fileAccess, FileShare fileShare, ref int attempts, int attemptWaitInMilliseconds)
{
try
{
return File.Open(filePath, fileMode, fileAccess, fileShare);
}
catch (UnauthorizedAccessException unauthorizedAccessException)
{
if (attempts <= 0)
{
throw unauthorizedAccessException;
}
else
{
Thread.Sleep(attemptWaitInMilliseconds);
attempts--;
return GetFileStream(filePath, fileMode, fileAccess, fileShare, ref attempts, attemptWaitInMilliseconds);
}
}
}