2009-07-02 11 views
7

Der folgende Code schlägt bei der Vorbedingung fehl. Ist das ein Fehler in Code-Verträgen?Fehler in Iteratoren mit Codeverträgen?

static class Program 
{ 
    static void Main() 
    { 
     foreach (var s in Test(3)) 
     { 
      Console.WriteLine(s); 
     } 
    } 

    static IEnumerable<int>Test (int i) 
    { 
     Contract.Requires(i > 0); 
     for (int j = 0; j < i; j++) 
      yield return j; 
    } 
} 

Antwort

2

Meine Vermutung ist, dass dies mit der verzögerten Natur der Iteratoren zu tun hat. Denken Sie daran, dass die Vertragsverarbeitung auf der endgültigen ausgegebenen IL erfolgt, nicht auf dem C# -Code. Dies bedeutet, dass Sie den generierten Code für Features wie Iteratoren und Lambda-Ausdrücke berücksichtigen müssen.

Wenn Sie diesen Code dekompilieren, werden Sie feststellen, dass "i" kein Parameter ist. Es wird eine Variable in der Klasse sein, die zur Implementierung des Iterators verwendet wird. Also eigentlich der Code sieht eher aus wie die folgenden

class IteratorImpl { 
    private int i; 
    public bool MoveNext() { 
    Contract.Require(i >0); 
    .. 
    } 
} 

Ich bin nicht sehr vertraut mit dem Vertrag API aber meine Vermutung ist der generierte Code ist viel schwieriger zu überprüfen.

+0

Warum sollten die Requires in BewegungNext anstelle des Konstruktors von IteratorImpl sein? –

+0

@pn, so entschied sich das C# -Team für die Implementierung von Iteratoren. Jeder Code, der im Hauptteil eines Iterators erscheint, endet in der MoveNext-Methode des generierten Codes. – JaredPar

+0

Meine Frage ist, ob das ein Fehler in Code-Verträgen ist oder nicht. Es sieht so aus, als ob der Code Contract Re Writer Iteratoren nicht versteht. –

0

Denken Sie daran, dass Iteratoren nicht ausgeführt werden, bis sie aufgezählt sind, und in einer speziellen Soße im Back-End kompiliert werden. Das allgemeine Muster, das Sie folgen sollten, wenn Sie Parameter überprüfen möchten, und dies gilt wahrscheinlich auch für Verträge, ist eine Wrapper-Funktion haben:

static IEnumerable<int> Test (int i) 
{ 
    Contract.Requires(i > 0); 
    return _Test(i); 
} 

private static IEnumerable<int> _Test (int i) 
{ 
    for (int j = 0; j < i; j++) 
     yield return j; 
} 

Auf diese Weise Test() wird die Prüfung der Parameter tun, wenn es aufgerufen dann Rückgabe _Test(), die eigentlich nur eine neue Klasse zurückgibt.

+0

Dies ist Workaround. Sollte ich jedoch meinen Code ändern oder ist das ein Fehler, der behoben wird? –

+0

Dies ist die Art, wie Iteratoren funktionieren - C# wird dieses Verhalten nicht ändern. Wenn Sie die Parameter der Methode überprüfen müssen und dies beim Aufruf nicht bei der Aufzählung tun möchten, müssen Sie sie in eine zweite Methode einbinden, die die Überprüfung durchführt. Ich weiß nichts über Contracts oder ob sie sich dafür entscheiden, besser mit Iteratoren zu arbeiten. – Talljoe

0

Hier ist ein blog post zu diesem Thema in Bezug auf Komponententests, Iteratoren, verzögerte Ausführung und Sie.

Verzögerte Ausführung ist das Problem hier.

0

.NET 4.0 (gerade versucht es) mit der endgültigen Version arbeiten Dieser Code wird in dem -Code Verträge in interators unterstützt werden, aber da ich es nicht immer richtig (lesen here mehr) funktioniert vor kurzem herausgefunden.

0

Dies könnte in der Vergangenheit ein Problem mit dem CodeContract-Rewriter gewesen sein. Aber die aktuelle Version scheint in Ihrem Beispiel gut zu funktionieren. Hier gibt es kein Problem mit Iteratoren/verzögerter Auswertung usw. Der Parameter i wird durch Wert erfasst und ändert sich während der Iteration nicht. Verträge sollten dies nur zu Beginn des Testaufrufs überprüfen, nicht während jeder Iteration.