参数validation最佳实践

想象一下,你有一个应用程序,它是你所有业务逻辑的某种前端 。 这个前端有很多依赖它的DLL,并且这些DLL中的方法可以在前端单次执行给定方法时重复调用。 如果您的应用程序的用户不直接访问这些DLL,您应该……

1)冒险(小)性能命中并validation每种方法中的参数,即使您最终可以validation相同参数约5次; 要么

2)冒险意外行为并假设,当您validation输入参数时,传入和传出内部代码的所有其他可能参数都是有效的(例如,既不为null也不为空)?

编辑:举个例子,假设你有一个Regex RegexA和一个方法

 internal bool Matches(string expression) { return RegexA.IsMatch(expression); } 

IsMatch将在null参数上抛出exception,但不在空字符串上抛出exception。 如果您事先知道空字符串永远不会与该正则表达式匹配,那么您之前if (String.IsNullOrEmpty(expression))应该使用if (String.IsNullOrEmpty(expression)) ,即使知道它可能在IsMatch框架方法中被validation为无效? 在这种情况下,您显然正在重复validation,但重复它还是冒风险更好?

通常参数检查非常便宜 ,即使被调用了数千次。 例如,如果值为null,则字符串或Collection为emtpy,数字在给定范围内。

但要注意检查可能很昂贵 ,所以请三思而行:在大字符串上评估正则表达式,检查文件是否存在,检查集合中的所有元素是否满足特定条件。

我也建议只检查公共或受保护的方法。 请注意,具有未经检查参数的所有公共方法都是潜在风险

编辑/另一个想法:如果一个方法不使用参数但只是将传递给另一个方法,那么你也可以省略检查。 只有实际使用这些参数的方法才能进行检查。

这是因为如果参数的要求发生变化,则需要在多个位置更改validation,从而存在不一致的风险。

除非参数的validation成本很高,否则我会选择#1。 快速失败的行为使您可以在很短的时间内捕获错误,这将比在每个方法开始时编写一些保护语句节省更多的时间。

您可能有兴趣帮助解决这个问题的一项技术是.NET的代码契约,它允许您创建准编译时检查,以确保没有人在没有确保输入与预期模式匹配的情况下调用方法。

我个人尝试使用Code Contracts,发现我的需求有一点点开销。 但是,我很欣赏语法,所以我创建了一个类来帮助这些保护语句,但这只能在运行时使用。 它的工作原理如下:

 public void ChangeUserName(int userId, string name) { Require.ThatArgument(userId > 0); Require.ThatArgument(!string.IsNullOrWhitespace(name, () => "Usernames must be non-empty strings"); var user = GetUser(userId); Require.That(user != null, () => new UserDoesNotExistException("No user exists with ID " + userId)); user.Name = name; ... } 

Resharper的Annotations是一项有助于这些检查的最终技术。 例如,请考虑以下方法:

 [CanBeNull] public User GetUser(int userId) { var user = ... // Get the user from the db return user; } 

通过告诉Resharper该方法可能返回一个空值,它会告诉您在尝试访问user.Name之前是否未对user进行空检查。 另一个注释可用于告诉Resharper Require.That(user != null)构成空检查。 你也可以像这样重写你的方法:

 [NotNull] public User GetUser(int userId) { Require.ThatArgument(userId > 0); var user = ... // Get the user from the db Require.That(user != null) return user; } 

通过将此方法标记为NotNull,Resharper可以自动告诉您user != null将始终解析为true因此您不必检查它。 您可以使用各种有趣的东西来简化validation。

作为库的作者,您不能假设消费者已对输入进行了适当的validation,因此您作为库作者希望在使用它们之前确保参数有效。

作为库的使用者,如果您知道哪些输入会导致库失败,为什么要将这些输入传递给该库? validation它们,以便您可以提示您的用户提供更好的输入或以其他方式取消您所处的任何过程。

在我看来,您可能是图书馆和消费者的作者并不是特别相关,因为这种关系可能会发生很大变化。

非常有趣的话题:)

通常,您应该实现低于用户界面的“validation外观”,并且在用户界面和外部服务通常访问的最低级别实现。

您可以在UI中检查null并validation输入,以避免无用的往返服务器,客户端validation是一个很好的做法,仍然不能相信调用者只传递有效值。

您可能会得到不同的意见,但在我看来……最好在两个层中进行validation。 在前面和业务逻辑(你称之为dll)