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

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

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

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]

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

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

         SELECT METHODS FROM ASSEMBLIES "FOO" 
         WHERE IsPropertyGetter AND ChangesObjectState 

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


person Joel in Gö    schedule 20.07.2010    source источник


Ответы (1)


Джоэл, это возможно благодаря Code Query с помощью возможностей 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()), и мы собираем все поля, назначенные всем этим методом.

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

введите здесь описание изображения

person Patrick from NDepend team    schedule 21.07.2010
comment
Спасибо, Патрик. Между прочим, можно ли использовать CQL, чтобы определить, потенциально ли метод используется через интерфейс? - person Joel in Gö; 22.07.2010
comment
Джоэл, теперь почти все возможно благодаря CQLinq (см. мой новый ответ). Просто задайте новый вопрос, если у вас есть другие потребности. - person Patrick from NDepend team; 13.06.2012