很多时候,在阅读源代码时我会看到类似的内容:
public void Foo(Bar bar)
{
if (bar == null) return;
bar.DoSomething();
}
我不喜欢这样,但我似乎错了,因为这种形式的防御性编程被认为是好的。是吗?例如,为什么bar null开头?是不是像这样做的检查类似于将绷带应用于问题而不是解决真正的解决方案?它不仅使函数与额外的代码行复杂化,而且还阻止程序员看到潜在的错误。
这是另一个例子:
public void Foo(int x)
{
int clientX = Math.Max(x, 0); // Ensures x is never negative
}
其他人看到这一点并看到防御性编程但我看到未来的错误,当程序员意外地传递负值并且程序突然中断并且没有人知道为什么因为这一点点逻辑吞噬了潜在的揭示异常。
现在,请不要混淆检查用户输入是否有效以及我在这里要求的内容。显然应该检查用户输入。我所要求的只是与不与用户或他或她的输入交互的代码。
答案 0 :(得分:2)
这int clientX = Math.Max(x, 0);
不是防御性编程 - 它伪装成潜在的问题!
防御性编程将是
if ( x < 0 )
throw new Exception ( "whatever" ); // or return false or...
并且绝对建议使用防御性编程...你永远不知道将来如何调用这段代码,所以你要确保它处理任何适当的东西,即它无法处理的东西必须尽早过滤掉必须“通知”呼叫者(例如,通过有意义的例外)......
答案 1 :(得分:1)
检查空值,因为尝试让空对象执行操作会触发异常。 “没什么”不能做“某事”。您的代码是这样的,因为您可以从不知道 100%的时间您的应用程序将处于什么状态。
话虽如此,对无效状态进行编码有好的和坏的方式,而你提供的那些例子并不完全是“好”的方式,尽管很难说它们脱离了上下文。
答案 2 :(得分:1)
很多时候你可能正在构建一个将被不同应用程序使用的组件,其中输入由不同编程风格的不同程序员提供,有些可能无法对传入组件/方法的数据进行彻底验证,因此防御性编程无论组件的用户做什么,在这种情况下抓住这个都是一件好事。
PS:但有一点,通常你不会像上面所示那样从方法返回,你会抛出一个合适的异常,也许是一个InvalidArgumentException或类似的东西。
答案 3 :(得分:1)
可能你经常看到这个:
public void Foo(Bar bar)
{
if (bar == null) throw new ArgumentNullException("bar");
bar.DoSomething();
}
这样,如果某人确实提供了null
值作为参数(可能是其他方法的结果),则不会在代码中的“某处”看到NullReferenceException,而是一个异常问题更清楚。
答案 4 :(得分:1)
在我看来,简单地返回无效输入类似于无声的尝试/捕获。 我仍然从其他代码片段验证进入我的函数的数据,但在遇到无效输入时总是抛出适当的异常。
答案 5 :(得分:0)
类似的东西,
int clientX = Math.Max(x, 0)
在某些情况下,可能会产生非常糟糕的影响,只是假设如果由于程序的其他某些位置的错误导致x为负,则会导致错误传播。我宁愿建议您记录并在发生不良情况时抛出异常(某些特定于业务逻辑的特殊类型)。