代码合同可以取代参数验证吗?

另一个人问了类似的问题,但答案并不明确。

我们的项目中有很多参数验证代码。 它们大多只在公共方法中为空参数检查:

if (param == null)
{
  throw new ArgumentNullException("param");
}

这些检查不是规范的一部分。 它们的主要用途是在编程错误的情况下获得更安全的堆栈转储。 如果这样一个简单的检查没有完成,你可以在一个其他库的中间随机选择一个NullReferenceException ,在栈中深入十个层次。 这使整个回溯到根本原因过程更长。 验证检查在语义上是清晰的,可以让您更早地发现问题。

既然我们这么做了,我们一直在寻找缩短这种检查方式的方法。 如:

ArgumentHelper.ThrowIfNull(param, "param");

或类似的语法。 我们希望避免基本上的开销。 if-throw语法使得代码不必要的长。

当我试验这种语法时,我注意到我正在收敛到一个类似于断言和代码合同的语法。 代码合同非常有吸引力,因为在开发周期的早期就有可能进行静态分析和捕获错误。

不过据我所知,代码合同并不是为了纠正“输入验证”问题或任何类型的验证问题而设计的。 因为它提出的例外并不意味着被捕获。 他们不是为了使调试更容易,因为默认行为是剥离发布版本中的这些检查的代码。 重写器看起来就像是一个黑客,它也不是很高效。

我所理解的所有“静态验证”和“参数验证”都有不同的目标,因此它们不能互换。 然而,在我的序幕中使用Code.Contracts if-then-throw在开始时听起来很自然,我不知道是否有错。

我的问题是:是否使用代码合同进行参数验证有效? 是否有一个明确的问题,我可以问任何验证代码,并提出一个答案,如:“是的,你可以使用代码合同来检查这个”和“不,你不能使用代码合同来检查”? 我如何做出区分?

我想出了“如果我删除了合同声明,该功能是否能够正常工作?”。 这是一个正确的方法吗?


嗯。 我是那个写出答案不清楚的人: - /。 我会用一种不同的方式来刺激它。

我会说你的方法是正确的,但有一个并发症。 从用户输入验证的角度来看这可能会有所帮助。 拿这个代码:

public void SetUserName(string userInput)
{
    if(String.IsNullOrWhitespace(userInput))
        throw new ValidationException("...");       
}

用户完全有可能忘记输入新的用户名; 空字符串是此方法可能接收的预期输入之一,并且它旨在处理它们。

现在让我们说我们添加一个处理用户输入的方法:

public string DoSomethingToUserName(string userInput)
{
    return userInput.ToLower();
}

此方法假定userInput不为null; 正如你所说,如果没有那么真实,它将无法工作。 如果传入一个空值,则有人犯了编程错误。 所以我们可以说,作为方法的接口的一部分,并得到静态验证器来警告我们是否有人被愚弄:

public string DoSomethingToUserName(string userInput)
{
    Contract.Requires(userInput != null);

    return userInput.ToLower();
}

如果这发生在运行时,这意味着不仅有人犯了编程错误,而且静态验证程序无法检测到问题,无论是由于某些反射使用或类似原因,还是仅仅因为其中存在错误。 因此,至少在公共场合的合同上打开合同重写总是一个好主意,以防万一。

所以,我们只使用合约来检查参数,只有在没有它们的情况下方法才会起作用。 它们是该方法成功运行的先决条件。 如果前提条件失败了,那么有人会出错,并且我们不希望发现异常,因为它揭示了一个错误。

当DoSomethingToUserName实际上没有实际操作用户名本身时,就会出现复杂情况:

public string DoSomethingToUserName(string userInput)
{
    return DoFirstThingToUserName(userInput);
}

private string DoFirstThingToUserName(string userInput)
{
    Contract.Requires(userInput != null);

    return userInput.ToLower();
}

现在,如果用户名为空,则DoFirstThingToUserName将失败; DoSomethingToUserName不在乎它是否是。 然而,没有人能够通过查看公共接口来判断userInput必须经过预先验证。 所以我们需要向外界宣布合同:

public string DoSomethingToUserName(string userInput)
{
    Contract.Requires(userInput != null);

    return DoFirstThingToUserName(userInput);
}

private string DoFirstThingToUserName(string userInput)
{
    Contract.Requires(userInput != null);

    return userInput.ToLower();
}

快速回答是否定的,您不能用代码合同替换输入验证。 主要区别在于输入验证不应该对无效数据进入引发异常,因为它不是特殊情况。 另一方面,违反合同总是会在系统中发出错误信号,因此应该抛出异常甚至粉碎你的应用(遵循快速失效原则)。

检查我的博客文章:代码合同与输入验证

链接地址: http://www.djcxy.com/p/23741.html

上一篇: Can code contracts replace parameter validation?

下一篇: Why CodeContracts Static Analyzer doesn't make warnings?