在我开始使用代码契约之前,我有时会在使用构造函数链接时遇到与参数验证相关的问题。
用一个(人为的)例子最容易解释:
class Test
{
public Test(int i)
{
if (i == 0)
throw new ArgumentOutOfRangeException("i", i, "i can't be 0");
}
public Test(string s): this(int.Parse(s))
{
if (s == null)
throw new ArgumentNullException("s");
}
}
我希望Test(string)
构造函数链接Test(int)
构造函数,为此我使用int.Parse()
.
当然,int.Parse()
不喜欢有一个 null 参数,所以如果s是 null 它会在我到达验证行之前抛出:
if (s == null)
throw new ArgumentNullException("s");
这使得该检查无用。
如何解决?好吧,我有时会这样做:
class Test
{
public Test(int i)
{
if (i == 0)
throw new ArgumentOutOfRangeException("i", i, "i can't be 0");
}
public Test(string s): this(convertArg(s))
{
}
static int convertArg(string s)
{
if (s == null)
throw new ArgumentNullException("s");
return int.Parse(s);
}
}
这有点繁琐,堆栈跟踪在失败时并不理想,但它可以工作。
现在,随着代码合同的出现,我开始使用它们:
class Test
{
public Test(int i)
{
Contract.Requires(i != 0);
}
public Test(string s): this(convertArg(s))
{
}
static int convertArg(string s)
{
Contract.Requires(s != null);
return int.Parse(s);
}
}
一切都很好。它工作正常。但后来我发现我可以做到这一点:
class Test
{
public Test(int i)
{
Contract.Requires(i != 0);
}
public Test(string s): this(int.Parse(s))
{
// This line is executed before this(int.Parse(s))
Contract.Requires(s != null);
}
}
然后如果我这样做var test = new Test(null)
,则在之前Contract.Requires(s != null)
执行。这意味着我可以完全取消测试! this(int.Parse(s))
convertArg()
所以,关于我的实际问题:
- 这种行为是否记录在任何地方?
- 在为这样的链式构造函数编写代码契约时,我可以依赖这种行为吗?
- 还有其他方法我应该解决这个问题吗?