我今天正在阅读 Uncle Bob 关于异常处理的书,我从处理空值中可以回忆起的是,方法不应该处理空值,因为它会使代码变得混乱。我有点困惑。我一直认为一个方法应该始终确保它的依赖项不为空(除非它们被注入到构造函数中并且构造函数保证了可空性)。例如,如果我有一个方法
public void SendMessage(IEmailSender emailSender, contactList list)
{
if(emailSender == null)
{
throw new ArgumentNullException("Failed to send
message.",MethodBase.GetCurrentMethod().GetParameters[0].Name);
}
if(list == null)
{
throw new ArgumentNullException("Failed to send
message.",MethodBase.GetCurrentMethod().GetParameters[1].Name);
}
// rest of code goes here
}
Run Code Online (Sandbox Code Playgroud)
我错过了什么吗?
小智 6
有两种观点:
一方面,通过您的方法,您总是会通过调用您的方法来告诉调用者他到底做错了什么。这对于调用者来说非常有用,因为当他收到你的异常时,他可以立即修复它。如果您编写第三方使用的 API 代码,那么这将是真实且有效的。
另一方面,如果您自己调用方法,则抛出异常的合理参数是,因为您希望能够在调用代码中使用 catch 块来处理这种情况!如果你没有理由在某个地方处理它,为什么还要抛出异常呢?我看到的唯一原因是,通过在 GlobalExceptionHandler 中捕获这些异常来记录详细的错误。
所以你看,我们这里有两类异常:一类是供开发人员使用,以避免错误使用 API,另一类是用作方法的错误结果。
如果您正在编写将被其他人使用的 API 代码,您的选择是不听 Bob 的;-)
对于那些没有读过 CleanCode 的人,Bob 建议两件事:
1.你不应该编写返回null的方法(以避免事后不必要的检查)。所以不要写这个:
var myObject = GetObjectThatDoesSomthing();
if(myObject != null)
{
myObject.DoSomething();
}
Run Code Online (Sandbox Code Playgroud)
...你应该能够这样写:
var myObject = GetObjectThatDoesSomething();
myObject.DoSomething();
Run Code Online (Sandbox Code Playgroud)
清洁工。
2.您不应该将 null 传递给您的方法,以避免在方法开头进行不必要的检查,如下所示:
public Point Add(Point p1, Point p2)
{
if(p1 == null) throw ArgumentException();
if(p2 == null) throw ArgumentException();
...
}
Run Code Online (Sandbox Code Playgroud)
这些规则的要点是:如果你坚持下去,你就会知道你不必编写这些空检查,并且你的代码会变得更干净、更容易阅读。但目前您正在使用第三方代码,您无法判断他们是否在 API 中应用了相同的规则,因此您需要再次开始预检查或后检查。当您为其他人编写 API 时,同样的事情:您的 API 的使用者如何知道您在编码时考虑了 Bob 规则......
我没有读过这本书,但我只能想象鲍勃叔叔提倡使用空对象模式而不是显式空引用处理。
例如,而不是
if(log != null)
log.Write("My log message");
Run Code Online (Sandbox Code Playgroud)
您可以创建一个ILogger包含方法的接口Write,并创建两个实现该接口的类: aNullLogger和 a FileLogger。该方法的实现NullLogger将有一个空的主体Write。
在我看来,这与示例中的显式前提条件验证不同