Vraiment essayer d’aimer CodeContracts en C #

Je joue enfin au rattrapage avec tout ce qui a été ajouté aux Frameworks .NET 3.5 / 4.0. Ces derniers jours, j’ai travaillé avec CodeContracts et j’essaie vraiment de les aimer. Je suis curieux de savoir ce que d’autres personnes pensent de l’implémentation de CodeContracts en C #? Plus précisément, comment les gens organisent-ils des choses comme les classes de contrat pour les interfaces, les méthodes de contrat pour les invariants de contrat, etc.?

J’aime la validation que les contrats fournissent, à première vue, ils ont fière allure. Avec quelques lignes simples, je peux obtenir une bonne vérification de la construction avant même d’exécuter mon code. Malheureusement, j’ai du mal à surmonter le sentiment que la façon dont les contrats de code sont mis en œuvre en C #, ils encombrent plus mon code que la documentation des contrats. Et pour tirer pleinement parti des contrats, je jette mon code avec des hypothèses et affirme, etc. (ce que je sais que certains diront que c’est une bonne chose); mais comme certains de mes exemples ci-dessous le montreront, cela transforme une seule ligne simple en 4 ou 5 lignes, et n’ajoute pas vraiment de valeur à mon avis par rapport à d’autres approches (affirmations, exceptions, etc.).

Actuellement, mes plus grandes frustrations sont:

Contrats d’interface:

[ContractClass(typeof(IInterfaceContract))] public interface IInterface { Object Method(Object arg); } [ContractClassFor(typeof(IInterface))] internal abstract class IInterfaceContract { private IInterfaceContract() { } Object IInterface.Method(Object arg) { Contract.Requires(arg != null); Contract.Ensures(Contract.Result() != null); return default(Object); } } 

Cela me semble être un tel embarras, je souhaite qu’il y ait un moyen plus propre de documenter les exigences, soit via des atsortingbuts ou une forme de prise en charge linguistique intégrée. Le fait que je doive implémenter une classe abstraite qui implémente mon interface, juste pour que je puisse spécifier que le contrat semble au mieux fastidieux.

Ballonnement du code:

 typeof(Action).MakeGenericType(typeof(Object); 

Nécessite plusieurs hypothèses pour vérifier les informations facilement disponibles. J’apprécie que tout ce que l’parsingur sache, c’est qu’il fonctionne sur Type et qu’il doit donc travailler sur ces connaissances limitées, mais cela me frustrait encore qu’une seule ligne de code me demande de réécrire

 var genericAction = typeof(Action); Contract.Assume(genericAction.IsGenericType); Contract.Assume(genericAction.GetGenericArguments().Length == 1); genericAction.MakeGenericType(typeof(Object)); 

Juste pour garder les choses documentées (oui, je sais que je peux utiliser ContractVerificationAtsortingbute pour désactiver cette méthode / classe, etc., ou SuppressMessageAtsortingbbute pour cibler des messages spécifiques, mais cela semble aller à l’encontre de votre objective.

En outre, prendre un cas comme

  public class MyClass : IInterface { private readonly Object _obj; public Object Property { get { Contract.Ensures(Contract.Result() != null); return _obj; } } public MyClass(Object obj) { Contract.Requires(obj != null); _obj = obj; } } 

obj doit être non nul et est défini sur un champ en lecture seule qui ne peut pas être modifié, mais je dois quand même append une méthode de “marquage” à ma classe pour que ma condition de propriété à ne pas renvoyer null puisse être prouvée:

 [ContractInvariantMethod] private void ObjectInvariant() { Contract.Invariant(_obj != null); } 

Il y en a plus, mais je me suis probablement douté, et j’apprécierais vraiment la perspicacité des gens beaucoup plus intelligents que moi pour m’aider à “aimer” les contrats de code et à faire disparaître ce sentiment d’encombrement du code. Toute idée sur la façon de mieux structurer le code, les solutions aux problèmes de gain, etc. serait grandement appréciée.

Merci!

Cela me semble être un tel embarras, je souhaite qu’il y ait un moyen plus propre de documenter les exigences, soit via des atsortingbuts ou une forme de prise en charge linguistique intégrée.

L’équipe de CC a déclaré que l’utilisation des atsortingbuts n’est tout simplement pas assez puissante, car vous ne pouvez pas y inclure des choses comme des lambda. Ils pourraient inclure des choses comme [NotNull] mais ils ont choisi de ne pas le faire parce qu’ils essaient de garder CC aussi général que possible.

L’une des raisons pour lesquelles CC est une bibliothèque (plutôt qu’une partie d’un C # étendu) est qu’elle est prise en charge dans tous les langages .NET.

Vous pouvez en savoir plus sur le raisonnement de l’équipe ici .

Pour ce qui est de l’utilisation de ce logiciel, j’ai juste gardé mes contrats d’interface dans le même fichier que l’interface, ce qui signifie que tout est documenté au même endroit. C’est quelque chose qui devrait être amélioré 🙂

Code Bloat […]

Votre deuxième plainte est probablement quelque chose qui peut être mis en œuvre – je vous suggère de l’afficher sur le forum des contrats de code . (EDIT: Semble quelqu’un a déjà , mais pas encore de réponses.)

Toutefois, les contrats sous-spécifiés nécessiteront toujours davantage d’hypothèses. Si vous rencontrez un tel cas dans le framework .NET, vous pouvez demander que des contrats soient ajoutés dans le thread Contrats manquants sur les bibliothèques .

En outre, prendre un cas comme […]

Cela a été abordé . Si vous avez une propriété auto, il vous suffit d’append un invariant non nul et les pré- / post-conditions seront générées:

 public class MyClass : IInterface { private Object Property { get; set; } [ContractInvariantMethod] private void Invariants() { Contract.Invariant(Property != null); } } 

Vous allez probablement vous retrouver avec d’autres invariants pour vos classes de toute façon, alors ce n’est pas une grosse affaire.

Oui, les contrats sont encombrants, mais je pense que cela en vaut la peine lorsque PEX lit les contrats de code et génère 25 tests unitaires et une couverture à 100% du code pour moi. Si vous n’avez pas encore utilisé PEX, alors vous manquez le plus grand avantage des contrats de code. Voici un guide de démarrage pour l’utilisation de PEX avec des contrats .

Je voulais vraiment l’aimer aussi, et je suis allé assez loin avec une implémentation dans un nouveau projet, jusqu’à ce que j’abandonne sous le poids des petits problèmes que je rencontrais.

J’aimerais que Microsoft ressuscite Spec #. Je sais que les contrats de code ont été écrits en tant que bibliothèque pour être disponibles dans toutes les langues, mais même la rapidité de l’parsing statique demande un support du compilateur.

Ce gars soulève les mêmes points: http://earthli.com/news/view_article.php?id=2183

En tout cas, il n’y avait pas un seul problème, mais une accumulation d’irritations:

  • L’parsing statique est très lente. Vous pouvez le configurer pour qu’il soit exécuté en arrière-plan, mais il est alors très facile de l’ignorer.
  • L’parsing statique n’est pas géniale, par exemple:
    • Il ne gère que le stream de contrôle simple
    • Il ne supposera pas que les champs en lecture seule sont invariants
    • Les collections ne sont pas gérées par le vérificateur statique (uniquement l’exécution)
  • Pas de contrat de délégué
  • Ne fonctionne pas bien avec Resharper à moins que vous ne supprimiez activement les avertissements de reprise d’une manière ou d’une autre
    • Resharper est optimiste. Il supposera qu’un paramètre de méthode n’est pas nul, jusqu’à ce que vous lui donniez un indice – comme Contract.Assume(thing != null)
  • Peut générer beaucoup d’avertissements et de suggestions, parfois issus d’une source ambiguë.
    • Au moment où un membre de l’équipe laisse passer son attention, le nombre d’avertissements deviendra écrasant pour tout le monde.
  • La nécessité de spécifier des classes de contrat abstraites pour les interfaces est un problème.
  • Les contrats d’exécution utilisent la réécriture IL, ce qui semble ne pas bien fonctionner avec d’autres solutions de réécriture comme PostSharp (je pense.
    • En raison de la réécriture IL, edit-and-continue ne peut pas être utilisé
  • Les contrats sont très étroitement liés au principe de substitution de liskov: les sous-types ne peuvent jamais assouplir les conditions préalables. Je veux dire, bon en principe, mais j’imagine que ce serait pénible de travailler avec un code tiers sous-traité.

J’ai créé un plugin pour Visual Studio 2010 qui peut vous faire gagner du temps en créant des contrats de code pour les interfaces et les classes abstraites: http://visualstudiogallery.msdn.microsoft.com/en-us/d491911d-97f3-4cf6-87b0-6a2882120acf

En fait, je trouve que l’implémentation de l’interface est excellente. Cela n’encombre pas votre interface ou votre code habituel, et vous pouvez le stocker dans un dossier de votre solution ou dans le même fichier de classe que votre interface, selon votre choix.

Pourquoi devez-vous append l’invariant d’object? Vous devez seulement append autant que vous le souhaitez.

Je comprends votre sharepoint vue sur le ballonnement du code, mais je suppose que c’est le compromis avec les contrats de code. Les contrôles / sécurité supplémentaires signifient des codes supplémentaires, du moins la manière dont ils sont actuellement mis en œuvre. J’essaierais de conserver autant de contrats que possible sur les interfaces, ce qui devrait au moins vous aider à atténuer la douleur liée au code.

Si vous êtes préoccupé par le gonflement du code, je vous encourage à regarder la programmation orientée aspect comme une alternative.

Cela vous permettra de spécifier le contrat dans l’aspect, puis d’envelopper les classes ou les méthodes des classes dans le contrat. Si votre contrat doit être utilisé dans plus d’un cours, ce serait un moyen plus efficace de le mettre en œuvre.

Malheureusement, le support pour AOP en C # n’est pas génial mais il y a quelques bibliothèques qui ajoutent cette fonctionnalité.

Et comme j’ai aussi le même sentiment envers les autres implémentations C # que celles que vous avez trouvées avec Contracts, tout semble bien au début jusqu’à ce que vous souleviez le capot et que vous commenciez à l’utiliser sérieusement.