经过一些研究,似乎人们普遍认为公共方法的参数应该被验证,而私有函数通常不需要。这让我有一些问题,但迄今为止我还没有找到一个令人满意的答案。
例子:
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;
}
想法:
如果在
DoWork()
内部i的非负要求发生变化怎么办?设计风险会留下过时的验证检查。我知道程序员有责任调整已经改变的函数的使用方式,但我不禁想知道是否有更好的方法来最小化错误的风险。那么不是从
DoSomething()
调用DoWork()
的不同调用呢?我们必须冗余地验证参数吗?
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)
的新函数会忘记验证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;
}
根据情况,这是我同时努力实现的一些目标:
- 在单个位置(可能在使用参数的函数内部)进行参数验证
- 尽早报告错误,而不是等到最后才因为错误的用户输入而导致大量代码执行失败
- 避免多次验证参数
- 避免对发布代码造成性能影响
如何取得平衡?哪种方法对您最有效?我将非常感激任何见解。
Contract.Requires()
前置条件在Mono 2.8
中得到支持,同时还有一个代码重写器来正确处理它们 - 这本身就非常有用。MS版本有一个设置,控制是否对公共+私有方法进行仪器化,或者仅对公共方法进行仪器化,并且您可以在调试和发布版本之间不同地选择。我希望Mono版本也支持这一点。 - Matthew Watson