Number of Types in the .NET Framework (2)

 

 


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,HttpBrowserCapabilities)

1129

201

Microsoft.JScript.BuiltinFunction.QuickCall(Object[],Object,

JSBuiltin,MethodInfo,VsaEngine)

1097

163

 

Can you
believe a method that calls 523 other methods? Actually, by decompiling it with
Reflector
(NDepend is integrated with Reflector, but also with VisualStudio), I found out
that CreatKnownElements() is actually
a giant switch that calls 523 properties getter. The same for GetResourceKey() and get_Resource(). So if you decompile UpProcess() you’ll see what looks like a method
that calls 201 others methods! But as you can see it is not by chance, it is
this kind of method that has to support hundreds of browser capabilities.

 

 

 

Most popular types and methods

 

Alternatively,
NDepend also support the Afferent Coupling (Ca)
that tells about the number of types that use a particular types, in other words the type popularity. Ao measure the most popular types and methods, NDepend
support a ranking metric. The same way as Google knows about most popular
pages, NDepend knows about the most popular types and methods.And the winner
are:

SELECT TOP 10 TYPES ORDER BY TypeRank DESC

 

Full Name

Type Rank

System.Runtime.InteropServices.ComVisibleAttribute

2471.8

System.Object

2338.9

System.Runtime.InteropServices.ClassInterfaceAttribute

2028.4

System.Void

1774.7

System.CLSCompliantAttribute

1137.5

System.Boolean

1032.3

System.Int32

1026.2

System.Runtime.InteropServices.GuidAttribute

948.05

System.String

900.83

System.Runtime.InteropServices.InterfaceTypeAttribute

889.51

 

Obviously Object, Boolean, Int32 or String are very popular, but more surprisingly,
the deep integration of .NET with win32 and COM make it so that we get several
interop types in the top 10 most popular types list.

 

And for the
methods…

 

SELECT TOP 10 METHODS ORDER BY MethodRank DESC

 

Full Name

Method Rank

System.Object..ctor()

16987.3

System.Exception.set_HResult(Int32)

6014.5

System.Exception.SetErrorCode(Int32)

5795.3

System.Environment.GetResourceString(String)

3831.7

System.Environment.GetResourceFromDefault(String)

3300.4

System.ArgumentNullException..ctor(String)

2965.8

System.String.get_Length()

2960.1

System.SystemException..ctor(String)

2862.4

System.Type.GetTypeFromHandle(RuntimeTypeHandle)

2412.9

System.Exception..ctor(String)

2001.5

 

 

 

Number of methods / fields for
types

 

Let’s go back
to more traditional metrics:

 

SELECT TYPES WHERE IsPublic AND (IsClass OR IsStructure OR IsInterface) ORDER BY NbMethods DESC

 

#Types: 14 625      Average: 14.35 methods     Std Dev: 31.73

 

SELECT TYPES WHERE IsPublic AND IsInterface ORDER BY NbMethods DESC

 

#Interfaces:
1 790      Average: 8.55 methods     Std Dev: 16.04

 

Here, I
would say that interfaces are a little bit too chatty, but consider that there
is a lot of interfaces such as com.ms.wfc.html.om.IHTMLStyle
that have 179 methods because of the
complexity of the underlying domain represented.

 

 

 

Depth of inheritance

 

SELECT TYPES WHERE IsPublic AND IsClass  ORDER BY DepthOfInheritance DESC

 

#Types: 12 324      Average: 2.32    Std Dev: 1.48

 

The depth
of inheritance is not a problem in the design of the .NET framework.
There are no abnormal values, the deepest complex graphical controls have a depth of
around 8.

 

 

 

Conclusion

 

Personally
I think that the quality is not really a matter of metrics but more a matter of
design, layering, componentization, in a word a matter of dependencies. The
quality also directly depends on good automatic testing and that’s why the next
version of NDepend will soon support test coverage metrics gathered from NCover and TeamSystem.
Imagine the power of this simple query:

 

SELECT METHODS WHERE CodeWasChanged AND PercentageCoverage < 100

 

If we just
take account of metric exposed in this post, I think that the .NET Fx v3.5 is
all in all well coded and designed. However, it contains many monsters that would make a sailor blush.
I like to think that when you are responsible for such a vital piece of code,
you sometime need to sacrifice good object models to some super complex methods
or types in order to get better performance at runtime. Are these monsters generated
methods? Are they 100% covered by automatic tests? Who knows?

 

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.grantpalin.com Grant Palin

    Nice one Patrick – some good insights!

    I’ve worked with .NET off and on over the last few years, but had no idea of the full size of the framework, until I saw these numbers. I am amazed at just how much material is in there!