How can I find which property getters have side effects using NDepend?
A familiar problem using VisualStudio is the mysterious calling of property getters. If these have side effects (the most common being of the form if (foo == null) foo = new foo(); return foo;
), then the fact that the debugger Locals and Watch windows call the properties - without even hitting any break points - can lead to unexpected effects when debugging.
There is a simple solution to this: just tag the property with the attribute
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
So how can I find getters which may have side effects in a large code base?
NDepend is the tool of choice for this kind of thing: using its CQL language I can find all the properties which, for example, directly change the state of their containing instance:
SELECT METHODS FROM ASSEMBLIES "FOO"
WHERE IsPropertyGetter AND ChangesObjectState
This only finds those getters which change a field directly: how can I find ones which change it indirectly, e.g. by calling an Initialize()开发者_开发问答
method?
Joel, this is possible thanks to Code Query through LINQ capabilities (CQLinq). Here is a CQLinq query that detect deep mutability for property getters. For each getters that provokes mutability, the code query shows the set of fields assigned.
// 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 }
This query is complex, mainly because I optimized it, to first keep only getters that themselves changing state, or that are calling directly or indirectly a method that is changing state (call to DepthOfIsUsingAny()).
Then, for each of this getter, we build the set of all methods called directly or indirectly (thanks to a call to FillIterative()), and we gather all fields assigned by all this method.
Concretely the query result looks like:
精彩评论