Wird garantiert, dass Codeverträge evaluiert werden, bevor verkettete Konstruktoren aufgerufen werden?

Bevor ich anfing, Codeverträge zu verwenden, hatte ich manchmal Probleme mit der Parametervalidierung, wenn ich Konstruktorketten verwendete.

Dies lässt sich am einfachsten mit einem (erfundenen) Beispiel erklären:

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");
    }
}

Ich will dasTest(string) Konstruktor zum Verketten derTest(int) Konstruktor, und dazu benutze ichint.Parse().

Na sicher,int.Parse() mag kein Null-Argument, also wenns ist null, wird geworfen, bevor ich die Validierungslinien erreiche:

if (s == null)
    throw new ArgumentNullException("s");

was diese Prüfung unbrauchbar macht.

Wie kann man das beheben? Nun, ich habe das manchmal gemacht:

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);
    }
}

Das ist ein bisschen umständlich und der Stack-Trace ist nicht ideal, wenn es fehlschlägt, aber es funktioniert.

Nun kommen Codeverträge und ich beginne, sie zu verwenden:

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);
    }
}

Alles schön und gut. Es funktioniert gut. Aber dann entdecke ich, dass ich das machen kann:

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);
    }
}

Und wenn dochvar test = new Test(null), dasContract.Requires(s != null) ausgeführt wirdVor this(int.Parse(s)). Das heißt, ich kann das abschaffenconvertArg() insgesamt testen!

Also weiter zu meinen eigentlichen Fragen:

Ist dieses Verhalten irgendwo dokumentiert?Kann ich mich beim Schreiben von Codeverträgen für solche verketteten Konstruktoren auf dieses Verhalten verlassen?Gibt es eine andere Möglichkeit, wie ich das angehen sollte?

Antworten auf die Frage(1)

Ihre Antwort auf die Frage