我会定期验证我的函数参数:
public static void Function(int i, string s)
{
Debug.Assert(i > 0);
Debug.Assert(s != null);
Debug.Assert(s.length > 0);
}
当然,检查在函数的上下文中是“有效的”。
这是常见的行业惯例吗?关于函数参数验证的常见做法是什么?
我会定期验证我的函数参数:
public static void Function(int i, string s)
{
Debug.Assert(i > 0);
Debug.Assert(s != null);
Debug.Assert(s.length > 0);
}
当然,检查在函数的上下文中是“有效的”。
这是常见的行业惯例吗?关于函数参数验证的常见做法是什么?
如果值无效或稍后将导致异常,则可接受的做法如下:
if( i < 0 )
throw new ArgumentOutOfRangeException("i", "parameter i must be greater than 0");
if( string.IsNullOrEmpty(s) )
throw new ArgumentNullException("s","the paramater s needs to be set ...");
所以基本参数异常列表如下:
参数异常 参数NullException 参数OutOfRangeException
您所写的是先决条件,也是按合同设计的基本要素。谷歌(或“StackOverflow”:) 这个词,你会发现很多关于它的好信息,也有一些不好的信息。请注意,该方法还包括后置条件和类不变量的概念。
让我们明确一点,断言是一种有效的机制。
当然,它们通常(并非总是)不会在发布模式下检查,因此这意味着您必须在发布代码之前对其进行测试。
如果断言保持启用并且断言被违反,则在某些使用断言的语言中(尤其是在 Eiffel 中)的标准行为是抛出断言违反异常。
如果您要发布代码库,那么未经检查的断言不是一种方便或可取的机制,也不是(显然)验证直接可能不正确的输入的方法。如果您有“可能不正确的输入”,则必须将输入验证层设计为程序正常行为的一部分;但是您仍然可以在内部模块中自由使用断言。
其他语言,如 Java,有更多显式检查参数并在错误时抛出异常的传统,主要是因为这些语言没有强大的“断言”或“契约式设计”传统。
(对某些人来说可能看起来很奇怪,但我发现传统上的差异是值得尊重的,不一定是邪恶的。)
另请参阅此相关问题。
您不应该使用断言来验证实时应用程序中的数据。据我了解,断言旨在测试该功能是否以正确的方式使用。或者该函数正在返回正确的值,即您得到的值是您所期望的。它们在测试框架中被大量使用。它们应该在系统部署时关闭,因为它们很慢。如果您想处理无效案件,您应该按照上面提到的海报明确地这样做。
任何可通过网络或进程间通信调用的代码都必须具有参数验证,否则它是一个安全漏洞 - 但您必须抛出异常 Debug.Assert 不会这样做,因为它只检查调试版本。
您团队中其他人将使用的任何代码也应该具有参数验证,因为这将帮助他们在他们向您传递无效值时知道这是他们的错误,这次您应该再次抛出异常,因为您可以添加一个很好的描述例外,并解释他们做错了什么以及如何解决它。
函数中的 Debug.Assert 只是为了帮助您进行调试,它是一个很好的第一道防线,但它不是“真正的”验证。
对于公共函数,尤其是 API 调用,您应该抛出异常。消费者可能会很高兴知道他们的代码中存在错误,并且有保证的方法是例外。
对于内部或私有函数,Debug.Assert 很好(但不是必需的,IMO)。您不会接受未知参数,并且您的测试应该通过预期输出捕获任何无效值。但是,有时,Debug.Assert 会让您更快地发现错误或防止错误。
对于不是 API 调用的公共函数,或受其他人调用它们的内部方法,您可以采用任何一种方式。我通常更喜欢公共方法的异常,并且(通常)让内部方法没有异常。如果内部方法特别容易被误用,则需要例外。
当您想要验证参数时,您不希望 4 个级别的验证必须保持同步(并为此支付性能损失)。因此,在外部接口上进行验证,并相信您和您的同事能够适当地调用函数和/或修复不可避免的错误。
大多数时候我不使用Debug.Assert,我会做这样的事情。
public static void Function(int i, string s)
{
if (i > 0 || !String.IsNullOrEmpty(s))
Throw New ArgumentException("blah blah");
}
警告:这是空气代码,我还没有测试过。
您应该使用 Assert 来验证程序假设;也就是说,对于这种情况
Assert 语句将允许您仔细检查是否永远不会达到不可能的状态。在没有验证的情况下您会感到舒服的地方使用它。
对于为函数提供错误参数的情况,但您可以看到它并非不可能接收这些值(例如,当其他人可以调用该代码时),您应该抛出异常(例如@Nathan W和@Robert Paulson)或优雅地失败(a la @Srdjan Pejic)。
我尽量不使用 Debug.Assert,而是写守卫。如果函数参数不是预期值,我退出函数。像这样:
public static void Function(int i, string s)
{
if(i <= 0)
{
/*exit and warn calling code */
}
}
我发现这减少了需要发生的争吵。
我不会谈论行业标准,但您可以将底部的两个断言组合成一行:
Debug.Assert(!String.IsNullOrEmpty(s));