Before I started using Code Contracts I sometimes ran into fiddlyness relating to parameter validation when using constructor chaining.
This is easiest to explain with a (contrived) example:
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");
}
}
I want the Test(string)
constructor to chain the Test(int)
constructor, and to do so I use int.Parse()
.
Of course, int.Parse()
doesn't like having a null argument, so if s is null it will throw before I reach the validation lines:
if (s == null)
throw new ArgumentNullException("s");
which renders that check useless.
How to fix that? Well, I sometimes used to do this:
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);
}
}
That's a bit fiddly, and the stack trace isn't ideal when it fails, but it works.
Now, along come Code Contracts, so I start using them:
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);
}
}
All well and good. It works fine. But then I discover that I can do this:
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);
}
}
And then if I do var test = new Test(null)
, the Contract.Requires(s != null)
is executed before this(int.Parse(s))
. This means that I can do away with the convertArg()
test altogether!
So, on to my actual questions:
- Is this behaviour documented anywhere?
- Can I rely on this behaviour when writing code contracts for chained constructors like this?
- Is there some other way I should be approaching this?