7

做一些研究,似乎人们普遍同意公共方法的参数应该被验证,而私有函数通常不需要。这让我产生了一些疑问,但到目前为止我还没有找到满意的答案。

例子:

public void DoSomething(int i)
{
    if (i < 0)
        throw new ArgumentOutOfRangeException("i");

    double d = DoWork(i);
}

private double DoWork(int i)
{
    double ret = ...; // some calculation
    return ret;
}

想法:

  1. 如果i内部非负的要求发生变化DoWork()怎么办?设计存在留下过时的验证检查的风险。程序员负责调整已更改函数的使用,我知道,但这让我想知道是否有更好的方法来最小化错误风险。

  2. 不同的电话DoWork()not fromDoSomething()怎么样?我们必须冗余地验证这些论点吗?

public void DoSomething(int i)
{
    if (i < 0)
        throw new ArgumentOutOfRangeException("i");

    double d = DoWork(i);
}

public void DoSomethingElse()
{
    int i = 5;
    if (i < 0)
        throw new ArgumentOutOfRangeException("i");

    double d = DoWork(i);
}

private double DoWork(int i)
{
    double ret = ...; // some calculation
    return ret;
}

这可以通过将检查放入它自己的函数中来稍微清理一下。然后存在调用的新函数DoWork(int i)会忘记 validate的风险i

public void DoSomething(int i)
{
    ThrowIfIntegerIsNegative(i);

    double d = DoWork(i);
}

public void DoSomethingElse()
{
    int i = 5;
    ThrowIfIntegerIsNegative(i);

    double d = DoWork(i);
}

static void ThrowIfIntegerIsNegative(int i)
{
    if (i < 0)
        throw new ArgumentOutOfRangeException("i");
}

private double DoWork(int i)
{
    double ret = ...; // some calculation
    return ret;
}

那比这更好吗?

public void DoSomething(int i)
{
    double d = DoWork(i);
}

public void DoSomethingElse()
{
    double d = DoWork(5);
}

private double DoWork(int i)
{
    if (i < 0)
        throw new ArgumentOutOfRangeException("i");

    double ret = ...; // some calculation
    return ret;
}

根据情况,这些是我试图同时实现的一些目标:

  1. 将参数验证放在一个地方(可能在使用参数的函数内部)
  2. 尽早报告错误(可能不想让一堆代码运行一个小时,最终因为一些错误的用户输入而失败)
  3. 避免多次验证参数
  4. 避免发布代码中的性能影响

你如何取得平衡?哪种方法最适合您?我将不胜感激任何见解。

4

3 回答 3

13

在公共方法中验证参数而不在私有方法中验证参数背后的逻辑大致如下:

  • 当使用无效参数调用公共方法时,这是您无法控制的编程错误。
  • 当使用无效参数调用非公共方法时,这是您控制范围内的逻辑错误。

这个逻辑是合理的:不需要浪费周期来验证模块内部生成的方法的参数。另一方面,私有方法总是可以假设它们的参数是有效的,因为你控制着私有方法的所有调用。

然而,当这些假设被违反时,捕捉情况是非常有益的。为此,使用运行时断言代替私有方法中的参数验证是一个非常好的主意。这会捕获来自外部调用者的无效调用,并通过断言提醒您来自您自己的方法的无效调用。

于 2013-06-06T18:27:09.630 回答
2

我喜欢在公共方法中使用异常进行验证:

public void Foo(int i)
{
    if (i < 0)
      throw new ArgumentOutOfRangeException("i");

    double d = DoWork(i);
}

而且我喜欢在私有方法中使用 Asserts 进行验证:

private double DoWork(int i)
{
    System.Diagnostics.Debug.Assert(i >= 0);
    // ....
}
  1. 这意味着在多个公共方法中重复验证。
  2. 它尽可能早地捕获无效参数。
  3. ArgumentOutOfRangeException由于参数名称的差异等原因,由于私有方法内部的参数无效而引发异常可能会使公共方法的调用者感到困惑。
  4. 通常,您的公共方法在调用您的私有方法之前会做一些额外的工作,以便节省这些工作。
  5. 使用单元测试来确保新方法不会错过任何验证检查。

要做的另一件事是将任何受保护的方法视为公共方法。

编辑:

这是一个错误验证的例子,它违反了上面的#3:

public void Foo(int distanceInMetres)
{
    double d = DoWork(distanceInMetres * 1000);
}

private double DoWork(int distanceInMillimetres)
{
    if (distanceInMillimetres < 0)
      throw new ArgumentOutOfRangeException("distanceInMillimetres");
    // ....
}

如果调用者Foo在参数“distanceInMillimetres”上看到异常,他会感到困惑,因为他调用了“Foo”,它采用了一个名为“distanceInMetres”的参数。

于 2013-06-06T18:25:26.173 回答
1

同意 dasblinkenlight 关于公共而非公共方法的声明。

如果你练习 TDD,你的私有方法只是重构代码并将一些公共方法提取为私有方法的结果。所以如果你用测试覆盖了公共方法,你的私有方法将被自动测试。

如果您不使用 TDD 但想验证逻辑没有违反,您可以使用不同的技术,例如 - 断言 ( http://msdn.microsoft.com/en-us/library/ttcc4x86.aspx ) 或代码合同(http://msdn.microsoft.com/en-us/library/dd264808.aspx)。

于 2013-06-06T18:40:11.077 回答