7
votes

Bug dans les itérateurs avec des contrats de code?

Le code suivant échoue sur la condition préalable. Est-ce un bug dans les contrats de code?

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


0 commentaires

5 Réponses :


2
votes

Je suppose que cela a à voir avec la nature retardée des itérateurs. N'oubliez pas que le traitement du contrat se produira sur la finale émise IL, pas le code C #. Cela signifie que vous devez envisager le code généré pour des fonctionnalités telles que les itérateurs et les expressions Lambda.

Si vous décompilez ce code, vous trouverez que "i" n'est pas en réalité un paramètre. Ce sera une variable de la classe qui est utilisée pour implémenter l'itérateur. Donc, le code ressemble donc plus à ce qui suit xxx

Je ne connais pas terriblement avec l'API contractuel, mais ma supposition est que le code généré est beaucoup plus difficile à vérifier.


4 commentaires

Pourquoi le requérant doit-il être sur le MOVENNEXT au lieu du constructeur d'itérateur implément?


@PN, c'est juste comment l'équipe C # a choisi de mettre en œuvre des itérateurs. Tout code qui apparaît dans le corps d'un itérateur se retrouvera dans la méthode MOVENEXT du code généré.


Ma question est de savoir si c'est un bogue dans les contrats de code ou non. On dirait que le contrat de code RE écrivain ne comprend pas les itérateurs.


@PN, je soupçonnerais qu'il est que l'auteur ne comprenne pas l'impact des itérateurs sur les contrats de code



0
votes

N'oubliez pas que les itérateurs ne sont pas courus avant d'être énumérées et sont compilées dans une sauce spéciale à l'arrière. Le motif général que vous devriez suivre si vous souhaitez valider les paramètres, ce qui est probablement vrai pour les contrats, est d'avoir une fonction d'emballage:

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


2 commentaires

C'est la solution de contournement. Cependant, devrais-je changer mon code ou est-ce qu'un bug qui sera corrigé?


C'est la façon dont les itérateurs travaillent - C # ne vont pas changer ce comportement. Si vous devez vérifier les paramètres de la méthode et que vous souhaitez le faire à l'invocation, non à l'énumération, vous devez l'envelopper dans une seconde méthode qui fait la vérification. Je ne connais pas les contrats ou si elles se fixeront mieux à travailler avec des itérateurs.



0
votes

voici un Blog post lié ​​à ce sujet très sujet concernant le test de l'unité, les itérateurs, l'exécution retardée et vous.

L'exécution retardée est le problème ici.


0 commentaires

0
votes

Ce code fonctionnera avec la version finale de .NET 4.0 (juste l'essayer) où contrats de code dans les intervenants sont pris en charge , mais comme je l'ai découvert récemment, il ne fonctionne pas toujours correctement (en savoir plus < Un href = "http://mokosh.co.uk/post/2010/06/26/net-4-code-contracts-and-berators/" rel = "nOfollow noreferrer"> ici ). < / p>


0 commentaires

0
votes

Cela a peut-être été un problème dans le codéconciation Redrer dans le passé. Mais la version actuelle semble faire de l'amende sur votre exemple. Il n'y a pas de problème ici avec itérateurs / évaluation retardée, etc. Le paramètre i est capturé par la valeur et ne changera pas pendant l'itération. Les contrats devraient vérifier cela uniquement au début de l'appel à tester, pas pendant chaque itération.


0 commentaires