2013-10-18 42 views
2

另一个人asked a similar question但答案并不清楚。代码合同可以取代参数验证吗?

我们的项目中有很多参数验证代码。他们大多是零参数检查公共方法只:

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

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

既然我们这么做了,我们一直在寻找更短的方法来做这样的检查。如:

ArgumentHelper.ThrowIfNull(param, "param"); 

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

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

然而,据我所知代码合同并没有设计来补救“输入验证”问题或任何类型的验证问题。 Because the exceptions raised by it are not meant to be caught。他们不是为了使调试更容易,因为默认行为是剥离发布版本中的这些检查的代码。 The rewriter just seems like a hack around it and it's not very performant either

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

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

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

+0

还检查了其抛出异常,并提供代码契约在一个非常干净的方式我的扩展方法:http://blog.rsuter.com/elegant-method-parameter-validation- with-code-contracts-support/ –

+0

@RicoSuter您的评论仍需要启用代码合同的运行时检查。看到我的答案为什么这是不好的:http://stackoverflow.com/a/25700857/54937 –

+0

不,你有合同和通常的抛出异常,即使运行时验证被禁用,抛出异常... –

回答

4

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

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

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

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

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

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

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

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(); 
} 
1

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

检查我的博客文章:Code contracts vs input validation

相关问题