使用VisualStudio的一个熟悉的问题是对属性getter的神秘调用。如果它们有副作用(最常见的形式是if (foo == null) foo = new foo(); return foo;
),那么调试器Locals和Watch窗口调用属性 - 甚至没有遇到任何断点 - 会在调试时导致意外的影响。< / p>
有一个简单的解决方案:只需使用属性
标记属性 [DebuggerBrowsable(DebuggerBrowsableState.Never)]
那么如何在大型代码库中找到可能有副作用的getter?
NDepend是这类事物的首选工具:使用其CQL语言,我可以找到所有属性,例如,直接更改其包含实例的状态:
SELECT METHODS FROM ASSEMBLIES "FOO"
WHERE IsPropertyGetter AND ChangesObjectState
这只能找到那些直接改变字段的getter:我怎样才能找到间接改变它的那些,例如:通过调用Initialize()
方法?
答案 0 :(得分:1)
Joel,这可能归功于Code Query through LINQ capabilities(CQLinq)。这是一个CQLinq查询,用于检测属性getter的深度可变性。对于引发可变性的每个getter,代码查询显示分配的字段集。
// 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 }
这个查询很复杂,主要是因为我优化了它,首先只保留自己改变状态的getter,或者直接或间接调用 正在改变状态的方法(调用{{3 }})。
然后,对于每个getter,我们构建直接或间接调用的所有方法的集合(感谢对DepthOfIsUsingAny()的调用),并且我们收集所有这些方法分配的所有字段。
具体来说,查询结果如下: