2010-07-20 34 views
3

Un problème familier avec VisualStudio est l'appel mystérieux de getters de propriété. Si ceux-ci ont des effets secondaires (le plus commun étant le formulaire if (foo == null) foo = new foo(); return foo;), alors le fait que les fenêtres Locales et Fenêtres du débogueur appellent les propriétés - sans même atteindre de points de rupture - peut conduire à des effets inattendus lors du débogage.Comment est-ce que je peux trouver quels getters de propriété ont des effets secondaires en utilisant NDepend?

Il y a une solution simple à cela: tout simplement marquer la propriété avec l'attribut

 [DebuggerBrowsable(DebuggerBrowsableState.Never)] 

Alors, comment puis-je trouver des getters qui peuvent avoir des effets secondaires dans une grande base de code?

NDepend est l'outil de choix pour ce genre de chose: en utilisant son langage CQL je peux trouver toutes les propriétés qui, par exemple, modifier directement l'état de leur instance contenant:

  SELECT METHODS FROM ASSEMBLIES "FOO" 
     WHERE IsPropertyGetter AND ChangesObjectState 

Cela ne trouve ces getters qui changent directement un champ: comment puis-je trouver ceux qui le modifient indirectement, par exemple en appelant une méthode Initialize()?

Répondre

1

Joel, ceci est possible grâce à Code Query through LINQ capabilities (CQLinq). Voici une requête CQLinq qui détecte la mutabilité profonde pour les getters de propriété. Pour chaque getter qui provoque la mutabilité, la requête de code montre l'ensemble des champs assignés.

// Restrict the iteration only on property getters 
// that are changing states or that call a method that changes state 
let propertyGetters = Application.Methods.Where(m => m.IsPropertyGetter) 

let methodsThatChangeState = 
    Application.Methods.Where(m => m.ChangesObjectState || m.ChangesTypeState) 

from m in propertyGetters.DepthOfIsUsingAny(methodsThatChangeState).DefinitionDomain 
      .Union(propertyGetters.Intersect(methodsThatChangeState)) 

// Find all methods called directly or indirectly by the property getter 
let methodsCalledIndirectly = 
     m.MethodsCalled.FillIterative(
      methods => methods.SelectMany(m1 => m1.MethodsCalled)) 
     .DefinitionDomain 
     .Union(m.ToEnumerable()) 

// Gather all field assigned that are not generated by the compiler 
let fieldsAssigned = methodsCalledIndirectly 
        .SelectMany(m1 => m1.FieldsAssigned) 
        .Where(f => !f.IsGeneratedByCompiler) 

where fieldsAssigned.Any() 
orderby fieldsAssigned.Count() descending 
select new { m, fieldsAssigned } 

Cette requête est complexe, surtout parce que je n'optimisé, d'abord de ne garder que getters qui se changement d'état, ou qui appellent directement ou indirectement une méthode qui est en train de changer l'état (appel à DepthOfIsUsingAny()). Puis, pour chacun de ces getter, nous construisons l'ensemble de toutes les méthodes appelées directement ou indirectement (grâce à un appel à FillIterative()), et nous rassemblons tous les champs assignés par toute cette méthode.

résultat Concrètement requête ressemble à:

enter image description here

+0

Merci Patrick. Incidemment, est-il possible d'utiliser CQL pour trouver si une méthode est potentiellement utilisée via une interface? –

+0

Joel, à peu près tout est possible maintenant grâce à CQLinq (voir ma nouvelle réponse) Il suffit de poser une nouvelle question si vous avez d'autres besoins. –

+0

Fantastique, continuez votre bon travail! –