参数验证最佳实践

本文关键字:最佳 验证 参数 | 更新日期: 2023-09-27 18:00:31

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

1) 冒着(小)性能命中的风险,验证每种方法中的参数,即使您最终可以验证相同的参数约5次;或

2) 冒着意外行为的风险,并假设在验证输入参数时,传递给内部代码和从内部代码传递的所有其他可能的参数都是有效的(例如,既不是null也不是空的)?

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

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

IsMatch将对null参数抛出异常,但不会对空字符串抛出异常。如果您事先知道一个空字符串永远不会与Regex匹配,那么您之前是否应该使用if (String.IsNullOrEmpty(expression)),即使知道它可能在IsMatch框架方法中被验证为无效?在这种情况下,你显然是在重复验证,但重复还是冒险更好?

参数验证最佳实践

通常参数检查非常便宜,即使调用了数千次。例如,测试一个值是否为null,一个字符串或Collection是否为emtpy,一个数字是否在给定范围内。

但请注意,检查可能会花费的费用,因此请三思:评估大字符串上的正则表达式,检查文件是否存在,检查集合中的所有元素是否符合特定标准。

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

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

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

除非参数的验证成本很高,否则我会选择#1。快速失败行为可以让你在很短的时间内发现错误,这将比在每个方法的开头写几个保护语句节省更多的时间。

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

我亲自尝试使用代码契约,发现对于我的需求来说,开销有点太多了。然而,我很欣赏语法,所以我创建了一个类来帮助处理这些保护语句,但它只在运行时起作用。它是这样工作的:

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该方法可能返回null值,它将知道如果您在尝试访问user.Name之前没有对user进行null检查,则会发出警告。另一个注释可用于告诉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,因此您不必检查它。您可以做各种有趣的事情来简化验证。

作为库的作者,您不能假设消费者已经对输入进行了正确的验证,因此作为库作者,您需要确保参数在使用它们之前是有效的。

作为库的使用者,如果你知道哪些输入会导致库失败,为什么要将这些输入传递给该库?根据它们进行验证,这样你就可以提示用户提供更好的输入,或者取消你正在进行的任何过程

在我看来,你可能是图书馆和消费者的作者这一事实并不特别相关,因为这种关系很可能会改变。

非常有趣的主题:)

一般来说,您应该实现一个低于用户界面的"验证门面",并且处于用户界面和外部服务通常访问的最低级别。

您也可以在UI中检查null和validate输入,以避免到服务器的无用往返,客户端验证是一种很好的做法,但您不能相信调用者只向您传递有效值。

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