Как я могу найти, какие свойства получателей имеют побочные эффекты, используя NDepend? - PullRequest
3 голосов
/ 20 июля 2010

Знакомая проблема использования VisualStudio - загадочный вызов методов получения свойств.Если они имеют побочные эффекты (наиболее распространенные из которых имеют вид if (foo == null) foo = new foo(); return foo;), то тот факт, что окна отладчика Locals и Watch вызывают свойства - даже не затрагивая точки останова, - может привести к неожиданным эффектам при отладке.

Для этого есть простое решение: просто пометьте свойство атрибутом

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]

Так как же найти геттеры, которые могут иметь побочные эффекты в большой базе кода?

NDepend является инструментом выбора для такого рода вещей: используя его язык CQL, я могу найти все свойства, которые, например, напрямую изменяют состояние содержащего их экземпляра:

         SELECT METHODS FROM ASSEMBLIES "FOO" 
         WHERE IsPropertyGetter AND ChangesObjectState 

Это находит только те получатели, которые изменяют поле напрямую: как я могу найти те, которые изменяют его косвенно, например, вызывая метод Initialize()?

1 Ответ

1 голос
/ 21 июля 2010

Джоэл, это возможно благодаря Запросу кода через возможности LINQ (CQLinq).Вот запрос CQLinq, который обнаруживает глубокую изменчивость для методов получения свойств.Для каждого метода получения, который вызывает изменчивость, запрос кода показывает набор назначенных полей.

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

Этот запрос сложен, главным образом потому, что я его оптимизировал, чтобы сначала сохранить только получатели, которые сами меняют состояние или которые прямо или косвенно вызывают метод, который изменяет состояние (вызов DepthOfIsUsingAny () ).

Затем для каждого из этих методов получения мы строим набор всех методов, вызываемых прямо или косвенно (благодаря вызову FillIterative ()), и мы собираем все поля, назначенные всем этим методом.

Конкретно результат запроса выглядит так:

enter image description here

...