コード コントラクトを使い始める前は、コンストラクター チェーンを使用するときに、パラメーターの検証に関連して厄介なことに遭遇することがありました。
これは (不自然な) 例で説明するのが最も簡単です:
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);
}
}
これは少し面倒です。失敗した場合のスタック トレースは理想的ではありませんが、機能します。
さて、Code Contracts が登場したので、私はそれらを使い始めました。
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()
それで、私の実際の質問に進みます:
- この動作はどこかに文書化されていますか?
- このような連鎖コンストラクターのコード コントラクトを記述するときに、この動作に依存できますか?
- これに近づくべき他の方法はありますか?