Patrick Smacchia [MVP C#]

Sponsors

The Lounge

Wicked Cool Jobs

News

  • NDepend v3 is fully integrated in Visual Studio, and is now available for download! Software dependencies visualization, 82 .NET software metrics, continuous rule validations, assembly version diff, declarative code queries and more ! http://ndepend.com

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
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?

 


Posted Wed, Mar 19 2008 2:05 PM by Patrick Smacchia

[Advertisement]

Comments

Community Blogs wrote Continued Story of Number of Types in the .NET Framework
on Wed, Mar 19 2008 11:55 AM

Recently Brad Abrams wrote a blog post about the number of types in the NET framework with some general

rascunho » Blog Archive » links for 2008-03-19 wrote rascunho &raquo; Blog Archive &raquo; links for 2008-03-19
on Wed, Mar 19 2008 4:33 PM

Pingback from  rascunho  &raquo; Blog Archive   &raquo; links for 2008-03-19

Reflective Perspective - Chris Alcock » The Morning Brew #57 wrote Reflective Perspective - Chris Alcock &raquo; The Morning Brew #57
on Thu, Mar 20 2008 4:17 AM

Pingback from  Reflective Perspective - Chris Alcock  &raquo; The Morning Brew #57

Grant Palin wrote re: Number of Types in the .NET Framework (2)
on Thu, Mar 20 2008 12:22 PM

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!

760 com wrote 760 com
on Sat, May 3 2008 3:38 PM

Pingback from  760 com

h 264 explained wrote h 264 explained
on Tue, May 6 2008 2:50 AM

Pingback from  h 264 explained

Add a Comment

(required)  
(optional)
(required)  
Remember Me?
Devlicio.us