While supporting NDepend, we got some interesting user feedback about the understanding on how dependencies are inferred. As shown below, sometime, the Dependency Matrix displays some cells weighted with 0. The first reaction is, what the hell is a dependency of weight 0?
On this screenshot, the Weight on Cells option is set to: Direct: # members. It means that if N members of a type/namespace/assembly are used by M methods of another type/namespace/assembly, then the corresponding blue cell will have a weight of N and its symmetric green cell a weight of M.
If you look carefully at the Information Panel while pointing the 0’s cell with the mouse, NDepend tells that there are no members of NamespaceUser4 using any members of NamespaceUsed, but still, there is a dependency between the 2 namespaces. This situation is unexpected! The explanation is that a type of the namespace NamespaceUser4 is using a type of the namespace NamespaceUsed without using any members. This is actually possible in many situations exposed in the code excerpt below, by passing an argument, declaring a field, implementing an interface, closing a generic type but also by tagging with an attribute, catching an exception…
If we switch the option Weight on Cells to Direct: # types, there is no more 0’s cell and we can see that some types are indeed using some others. Actually, when Weight on Cells is set to something else than Direct: # members/methods/fields it is not possible to have 0’s cells because the way the Common Type System is made, it is not possible to have a dependency not involving some types.
Issue with const values
Sometime, NDepend won’t report something that user considers as a dependency between 2 code elements. This case can happen when there are some enumerations values or some const fields. For example::
This screenshot shows that NDepend didn’t infer a dependency between the namespaces!
It is actually not a bug but a consequence of a C#/VB.NET compiler optimization. I quote the master Jeffrey Richer from his excellent book CLR via C# page 177:
(…)When code refers to a constant symbol, compilers look up the symbol in the metadata of the assembly that defines the constant, extract the constant’s value, and embed the value in the emitted IL code. (…)
It means that by looking at the IL code, one cannot infer the dependency between a method and a constant used. Moreover, it is worth mentioning that enumeration’s values are compiled as constant. Althought NDepend parses source code to get some metrics about comments for example, the bulk of information gathered from the code base comes from the IL. This behavior has many appealing advantages, the analysis is not impacted by the way code is formatted neither by special language syntax peculiarities, data obtained from source written in different languages can be compared, one can check that a tier library code base abides by dozens of quality tenets even without having access to source files, NDepend can collaborate with IL focused tools like Reflector but can still collaborates with source code focused tools like Visual Studio…
Concerning this const compiler optimization, I would like to precise that it can be a source of major problems. Let’s quote Jeffrey again:
(…)These constraints also mean that constants don’t have a good cross-assembly versioning story, so you should use them only when you know that the value of a symbol will never change(…)
What Jeffrey means is that if assembly A is using some constants of assembly B, if constant changes in B and B is recompiled, if A is not recompiled it will still contains the old constant values.
Finally, this const compiler optimization has been a source of complexity while developing the NDepend assembly comparison feature. Imagine that 1000 methods are using values of an enumeration (which is a real-world case). If for some reasons values of the enumeration are changing, do you want to be advised that 1000 methods have changed? Indeed, the IL code of the methods have been updated with the new values but still, the C# or VB.NET source code of these methods haven’t changed. Hopefully we made our algorithms a bit smart and NDepend can detect this case and not report it to the user.