I am
impressed by the buzz done around my last post on Number of Types in the .NET Framework. Actually it was just a quick
post that I wrote after having read the Brad
Abrams Number of Types in the .NET
Framework post to see what NDepend
would report on metrics on the .NET Fx v3.5. I didn’t expect that this would
turn in a debate around the completeness and the download size of the .NET
Fxv3.5 and even Java Vs. .NET. As a heavy user of .NET since the early beginning back in 2001 and as a big
fan of this platform, my opinion is biased even thought I think that some
parts are perfectible such as:
- Collections
are duplicated (for legacy reason)
- The design
of the IO API that after 7 years I still found not being intuitive
But so far,
these small issues don’t matter when I take account of the benefit of working
with .NET:
- A super
innovative language team able to bring a ground-breaking enhancement every 2
years.
- A powerful
compiler, able to compile my 60K lines of C# code in less than 5 seconds.
- Maybe the best debugger of all times.
- An optimized
CLR, close to the hardware, that let me for example use pointers from managed
code whenever I want
- An active
community able to make diamonds such as Mono.Cecil .
Instead of
rambling on this sensible debate, I prefer exposing more metrics relative to the
.NET Fx v.3.5.
Size of methods
The size of
methods is computed in terms of number of IL instructions.
As the .NET Fx v3.5 is compiled with optimization, you can guess the equivalent
number of lines of C# or VB.NET code by dividing values by 5.
SELECT METHODS WHERE NbILInstructions > 0 AND !IsClassConstructor ORDER BY NbILInstructions DESC
#Methods: 336 908 Average: 24.66 IL Instructions Std Dev: 58.05
SELECT TOP 5 METHODS WHERE NbILInstructions > 0 AND !IsClassConstructor ORDER BY NbILInstructions DESC
|
Full Name
|
# IL
instructions
|
|
System.Windows.Markup.KnownTypes.GetKnownTypeConverterIdForProperty( KnownElements,String)
|
6228
|
|
MS.Internal.Markup.KnownTypes.GetKnownTypeConverterIdForProperty( KnownElements,String)
|
6228
|
|
System.Security.Cryptography.RIPEMD160Managed.MDTransform( UInt32*,UInt32*,Byte*)
|
5294
|
|
MS.Internal.Markup.TypeIndexer.InitializeOneType(KnownElements)
|
3766
|
|
System.Web.Configuration.BrowserCapabilitiesFactory. PopulateBrowserElements(IDictionary)
|
3596
|
The class
constructors are not taken account because they biase the result since they
all the bunch of code that initialize the value of static fields.
Cyclomatic Complexity and
nested depth of methods
As we don’t
have source code of the .NET Fx, we infer the Cyclomatic Complexity from the IL
code as explained here.
We generally obtain values slightly higher than the Cyclomatic Complexity
obtained from source code.
The second
practical metric to measure complexity is the Nesting Depth, here also inferred
from IL, whose definition is available read here.
SELECT METHODS WHERE NbILInstructions > 0 ORDER BY ILCyclomaticComplexity DESC,ILNestingDepth DESC
#Methods: 341 842 Average IL Cyclomatic Complexity: 1.68 Std
Dev: 5.48
Average IL Nesting
Depth: 0.68 Std
Dev: 1.80
These
numbers attest from a great overall quality, but it is still possible to find
some monsters (I don’t know if these monsters are generated method or
handcrafted methods?):
SELECT TOP 5 METHODS WHERE NbILInstructions > 0 ORDER BY ILCyclomaticComplexity DESC
|
Full Name
|
# IL instructions
|
IL Cyclomatic Complexity (ILCC)
|
|
System.Windows.Markup.KnownTypes.GetKnownTypeConverterIdForProperty( KnownElements,String)
|
6228
|
872
|
|
MS.Internal.Markup.KnownTypes.GetKnownTypeConverterIdForProperty( KnownElements,String)
|
6228
|
872
|
|
MS.Internal.Markup.TypeIndexer.InitializeOneType(KnownElements)
|
3766
|
761
|
|
System.Windows.Markup.TypeIndexer.InitializeOneType( KnownElements)
|
3046
|
760
|
|
System.Windows.Markup.KnownTypes.CreateKnownElement(KnownElements)
|
1729
|
549
|
SELECT TOP 5 METHODS WHERE NbILInstructions > 0 ORDER BY ILNestingDepth DESC
|
Full Name
|
# IL instructions
|
IL Nesting Depth
|
|
Microsoft.JScript.Convert.Coerce2WithNoTrunctation(Object,TypeCode)
|
1539
|
268
|
|
System.Windows.Markup.KnownTypes.GetKnownPropertyAttributeId( KnownElements,String)
|
1979
|
190
|
|
MS.Internal.Markup.KnownTypes.GetKnownPropertyAttributeId( KnownElements,String)
|
1979
|
190
|
|
System.Web.Configuration.BrowserCapabilitiesFactory.UpProcess( NameValueCollection,HttpBrowserCapabilities)
|
1129
|
181
|
|
MS.Internal.Markup.KnownTypes.GetKnownTypeConverterIdForProperty( KnownElements,String)
|
6228
|
172
|
Number of Parameters of public
methods and Variables
The number
of parameters of a method represents an easy way to measure quality:
SELECT METHODS WHERE IsPublic ORDER BY NbParameters DESC
#Methods: 219 234 Average #Parameters: 0.98 Std Dev: 1.32
Here also
the overall quality is pretty good, but here also it is easy to find terrific values:
SELECT TOP 5 METHODS WHERE IsPublic ORDER BY NbParameters DESC
|
Full
Name
|
#
Parameters
|
|
MS.Internal.PtsHost.UnsafeNativeMethods.PTS+FormatLine. BeginInvoke(IntPtr,IntPtr,IntPtr,Int32,Int32,IntPtr,UInt32,Int32, Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,IntPtr&, Int32&,IntPtr&,Int32&,PTS+FSFLRES&,Int32&,Int32&,Int32&,Int32&, Int32&,Int32&,AsyncCallback,Object)
|
31
|
|
MS.Internal.PtsHost.UnsafeNativeMethods.PTS+ReconstructLineVariant. BeginInvoke(IntPtr,IntPtr,IntPtr,Int32,Int32,IntPtr,Int32,UInt32,Int32,Int32 ,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,IntPtr&,IntPtr& ,Int32&,PTS+FSFLRES&,Int32&,Int32&,Int32&,Int32&,Int32&,Int32&, AsyncCallback,Object)
|
31
|
|
MS.Internal.PtsHost.UnsafeNativeMethods.PTS+FormatLineForced. BeginInvoke(IntPtr,IntPtr,IntPtr,Int32,Int32,IntPtr,UInt32,Int32,Int32 ,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,IntPtr&,Int32& ,IntPtr&,PTS+FSFLRES&,Int32&,Int32&,Int32&,Int32&,Int32&, AsyncCallback,Object)
|
29
|
|
MS.Internal.PtsHost.UnsafeNativeMethods.PTS+ReconstructLineVariant. Invoke(IntPtr,IntPtr,IntPtr,Int32,Int32,IntPtr,Int32,UInt32,Int32,Int32, Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,IntPtr&,IntPtr&,Int32& ,PTS+FSFLRES&,Int32&,Int32&,Int32&,Int32&,Int32&,Int32&)
|
29
|
|
MS.Internal.PtsHost.UnsafeNativeMethods.PTS+FormatLine.Invoke(IntPtr, IntPtr,IntPtr,Int32,Int32,IntPtr,UInt32,Int32,Int32,Int32,Int32,Int32,Int32 ,Int32,Int32,Int32,Int32,Int32,IntPtr&,Int32&,IntPtr&,Int32&, PTS+FSFLRES&,Int32&,Int32&,Int32&,Int32&,Int32&,Int32&)
|
29
|
And the
following CQL query returns 568 methods!
SELECT METHODS WHERE IsPublic AND NbParameters > 8
Similarly,
you can also measure the quality from the number of internal variables used by
a method and we obtain some similar results:
SELECT METHODS WHERE IsPublic AND NbILInstructions > 0 ORDER BY NbVariables DESC
#Methods: 184 149 Average #Parameters: 0.86 Std Dev: 2.14
SELECT TOP 5 METHODS WHERE IsPublic ORDER BY NbVariables DESC
|
methods
|
# Variables
|
|
java.awt.GridBagLayout.GetLayoutInfo(Container,Int32)
|
105
|
|
Microsoft.VisualBasic.CompilerServices.VBBinder.BindToMethod( BindingFlags,MethodBase[],Object[]&,ParameterModifier[],CultureInfo,String[],Object&)
|
97
|
|
javax.swing.plaf.basic.BasicLookAndFeel.initComponentDefaults(UIDefaults)
|
64
|
|
System.ServiceModel.Description.DispatcherBuilder.InitializeServiceHost( ServiceDescription,ServiceHostBase)
|
62
|
|
System.Windows.Forms.ControlPaint.DrawBorder( Graphics,Rectangle,Color,Int32,ButtonBorderStyle,Color,Int32,ButtonBorderStyle,Color, Int32,ButtonBorderStyle,Color,Int32,ButtonBorderStyle)
|
61
|
Efferent Coupling
NDepend is
far from being just a metric software and can help you deal with dependencies, layering
and componentization, can compare 2 snapshots of your code base, can check some
custom rules on your design and code and more (see the list of features here). It does support some exotic (but still useful) metrics like Efferent Coupling
(Ce).
The Efferent Coupling for a particular type is the number of types it directly
depends on. As I explained in a previous post, the more types a given type is using, the more responsibilities it has.
SELECT TYPES WHERE NbILInstructions > 0 ORDER BY TypeCe DESC
#Types: 28 738 Average # of types used: 22.88 Std
Dev: 20.71
Here I
found the average a little high, but we have to take account that primitive
types such as int, string or bool are taken account in this result.
And who are
the monsters types?
SELECT TOP 5 TYPES WHERE NbILInstructions > 0 ORDER BY TypeCe DESC
|
Full
Name
|
#
IL instructions
|
Efferent coupling at
type level (TypeCe)
|
|
System.Windows.Markup.KnownTypes
|
12593
|
582
|
|
System.Windows.Forms.Control
|
18246
|
332
|
|
System.Windows.Forms.DataGridView
|
67320
|
331
|
|
MS.Internal.PtsHost.UnsafeNativeMethods.PTS
|
249
|
264
|
|
System.Windows.Forms.ListView
|
10824
|
254
|
NDepend
also support Ce on methods
(and here we get the number of methods
used):
SELECT METHODS WHERE NbILInstructions > 0 ORDER BY MethodCe DESC
#Methods: 341 842 Average # of methods used: 3.41 methods Std
Dev: 5.65
SELECT TOP 5 METHODS WHERE NbILInstructions > 0 ORDER BY MethodCe DESC
|
methods
|
#
IL instructions
|
Efferent coupling at
method level (MethodCe)
|
|
System.Windows.Markup.KnownTypes.CreateKnownElement(KnownElements)
|
1729
|
523
|
|
System.Windows.SystemResourceKey.GetResourceKey(Int16)
|
459
|
225
|
|
System.Windows.SystemResourceKey.get_Resource()
|
595
|
210
|
|
System.Web.Configuration.BrowserCapabilitiesFactory.UpProcess( NameValueCollection,HttpBrowserCa |