如何在C#中开发库时进行正确的异常处理
本文关键字:异常处理 开发 | 更新日期: 2023-09-27 18:25:10
假设我想创建一个DLL库,以一种易于使用的方式提供通用文件系统服务。这个库可以在第三方.NET应用程序中使用。假设这个库只向公众提供这个接口:
public interface IFileSystemService
{
void CreateDirectory(string path);
void DeleteDirectory(string path);
bool DirectoryExists(string path);
IEnumerable<string> EnumerateDirectories(string path);
void DeleteFile(string path);
bool FileExists(string path);
void CopyFile(string sourcePath, string destinationPath, bool overwriteExisting);
// ... and other methods
}
当使用System.IO命名空间(例如FileInfo、DirectoryInfo、Directory、File等)中的标准.NET类来实现此接口时,异常处理的最佳实践是什么?如何使这个库健壮并易于客户端代码使用?我如何做到这一点,以便用于错误处理的客户端代码干净而不过于复杂?
最佳实践是尽你所能从异常中恢复,但如果你什么都做不了,就让异常传播到调用者,让他们处理它。
至于如何使库健壮且易于使用,请尽可能多地为库编写文档。您可以使用XML文档将哪些异常直接包含在DLL 的元数据中
(评论来自Directory.CreateDirectory(string)
)
public interface IFileSystemService
{
/// <summary>
/// Creates all directories and subdirectories as specified by <paramref name="path"/>. ///
/// </summary>
///
/// <returns>
/// A <see cref="T:System.IO.DirectoryInfo"/> as specified by <paramref name="path"/>. ///
/// </returns>
/// <param name="path">The directory path to create.</param>
/// <exception cref="T:System.IO.IOException">The directory specified by <paramref name="path"/> is read-only.</exception>
/// <exception cref="T:System.UnauthorizedAccessException">The caller does not have the required permission.</exception>
/// <exception cref="T:System.ArgumentException"><paramref name="path"/> is a zero-length string, contains only white space, or contains one or more invalid characters as defined by <see cref="F:System.IO.Path.InvalidPathChars"/>.
///
/// -or-
/// <paramref name="path"/> is prefixed with, or contains only a colon character (:).</exception>
///<exception cref="T:System.ArgumentNullException"><paramref name="path"/> is null.</exception>
///<exception cref="T:System.IO.PathTooLongException">The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters and file names must be less than 260 characters.</exception>
///<exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid (for example, it is on an unmapped drive).</exception>
///<exception cref="T:System.NotSupportedException"><paramref name="path"/> contains a colon character (:) that is not part of a drive label ("C:'").</exception>
void CreateDirectory(string path);
//Do similar comments for the rest.
void DeleteDirectory(string path);
bool DirectoryExists(string path);
IEnumerable<string> EnumerateDirectories(string path);
void DeleteFile(string path);
bool FileExists(string path);
void CopyFile(string sourcePath, string destinationPath, bool overwriteExisting);
// ... and other methods
}
当使用System.IO命名空间中的标准.NET类(例如FileInfo、DirectoryInfo、Directory、File等)来实现此接口时,异常处理的最佳实践是什么
就我而言,最好的做法是让异常冒出来。作为一个"框架"实现者,您不知道API用户的异常处理的意图是什么。因为您不知道这个用户的意图,所以最好让异常被抛出。
- 通过让异常冒泡,您可以获得异常实际发生位置的完整堆栈跟踪
- 您还允许基于一个上下文处理想要捕获的异常
根据Eric Lippert的博客文章,我再也找不到Vexing Exceptions了,我记得他说最好看到这样的代码:
if (condition) throw new Exception("message");
比看:
try { something(); }
catch (Exception ex) { throw new Exception(); }
因为每次重新引发异常时都要重新设置堆栈跟踪。或者,你可以这样做:
try { something(); }
catch (Exception ex) {
handleTheExceptionInternallyForYourPurpose();
throw ex; // simply let the exception flow go its way to the top, to the caller.
}
另外,这完全取决于你对行为的期望。假设您不希望允许空值作为path
参数,那么在这种情况下,您可能希望抛出ArgumentNullException
。
public void CreateDirectory(string path) {
if (path == null) throw new ArgumentNullException("path");
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("path");
// create your directory here based on the path provided...
}
如何使此库健壮并易于客户端代码使用
我打赌你不能为其他程序员编程。您可能拥有世界上最好的库,经验较少或不尊重SRP的库可能会导致代码混乱。遵循一些准则并尊重最佳实践和原则是每个程序员的责任。可悲的是,这些原则很少得到遵守,至少在我这个世界的角落里是这样。无论你做什么,根据你的背景,总会有更好的方法来做事。
如何做到这一点,以便用于错误处理的客户端代码干净而不过于复杂
复杂的东西对代码来说总是很复杂的,所以没有办法逃脱。通过坚持SOLID原则,并使用明天的最佳实践,您将拥有一个简单易懂的库。
XML注释对于库也是一种很好的方法,这样IntelliSense就可以在键入时显示它。