Analyzing the .NET Framework 2.0 with NDepend and CQL

by Patrick Smacchia

Content

Introduction

NDepend 2.0 has just been released (download it at http://www.NDepend.com). It is a free .NET tool for developers which analyses IL code and produces reports containing numerous code metrics, warnings and diagrams. The 2.0 version provides great enhancements such as an interactive view of your application and a new language dedicated to query and constraint the structure of your code: Code Query Language (CQL).

The purpose of this article is to expose some quantitative and qualitative information about the .NET framework 2.0 obtained from some CQL queries. We'll consider that the .NET 2.0 framework is made of the following 15 assemblies: System.Windows.Forms, System.Web, mscorlib, System, System.Data, System.XML, System.Deployment, System.Web.Services, System.Data.SqlXml, System.Drawing, System.Runtime.Remoting, System.Transactions, System.Messaging, System.Security, System.Drawing.Design



We choose to analyse the .NET framework 2.0 since obviously, every developers know it. However we precise that NDepend 2.0 can be used to analyse any .NET application (i.e any set of assemblies) no matter it is a console, windows form or ASP.NET application and no matter it is written with C# or VB.NET or any other .NET language.

Getting started

Let's have a first glimpse of the size of each assembly thanks to the following CQL query:

SELECT ASSEMBLIES ORDER BY NbILinstructions DESC, NbNamespaces DESC, NbTypes DESC, NbMethods DESC
Name # IL instructions # Namespaces # Types # Methods
System.Windows.Forms 560331 20 2214 27754
System.Web 478412 24 1874 22638
mscorlib 429203 53 2319 21040
System 357693 39 1924 15095
System.XML 297142 12 849 10331
System.Data 291324 14 860 11722
System.Data.SqlXml 117347 10 433 4520
System.Web.Services 97166 10 329 2603
System.Drawing 58669 11 298 3930
System.Runtime.Remoting 42185 8 182 1419
System.Deployment 38830 8 466 2094
System.Security 30752 7 203 1256
System.Transactions 23726 5 203 1381
System.Messaging 20586 5 104 966
System.Drawing.Design 7690 3 39 285
Sum: 2851056 229 12297 127034
Average: 190070.4 15.3 819.8 8468.9
Minimum: 7690 3 39 285
Maximum: 560331 53 2319 27754
Standard deviation: 185867.8 13.4 800.1 8851.2
Variance: 3.454684E+10 180.5 640139.4 7.834298E+07

You can see that the CQL syntax is close to the SQL one since it supports the SELECT TOP FROM WHERE ORDER BY pattern. This similarity comes from the fact that NDepend consider metadata and structure of your application as a database. From this point of view, CQL is used to query such database.

Delivered with NDepend 2.0, you'll find the VisualNDepend tool which provides a more intuitive idea of the topology of your application thanks to a treemap view:

VisualNDepend also offers a CQL query editor with intellisense and verbose syntax error description:

Notice also that the NDepend report contains a diagram of assemblies' dependencies:

CQL Queries vs. CQL Constraints

CQL can help you know where you should focus your energy to enhance the quality of your code. For example you can use the following CQL query to know where your biggest methods are:

SELECT TOP 5 METHODS ORDER BY NbILinstructions DESC
Name # IL instructions Full Name
MDTransform(Uint32*,Uint32*,Byte*) 5294 System.Security.Cryptography.RIPEMD160Managed.
MDTransform(Uint32*,Uint32*,Byte*)
PopulateBrowserElements(IDictionary) 3596 System.Web.Configuration.BrowserCapabilitiesFactory.
PopulateBrowserElements(IDictionary)
.cctor() 2462 System.Text.RegularExpressions.RegexCharClass..cctor()
.ctor() 2462 System.Text.RegularExpressions.RegexCharClass..ctor()
ImportAccessorMapping(MemberMapping,FieldModel,
XmlAttributes,String,Type,Boolean,Boolean)
2344 System.Xml.Serialization.XmlReflectionImporter.
ImportAccessorMapping(MemberMapping,FieldModel,
XmlAttributes,String,Type,Boolean,Boolean)

You might prefer using a threshold:

SELECT METHODS WHERE NbILinstructions > 200 ORDER BY NbILinstructions

(in the case of the .NET framework 2.0, 1107 methods are selected on 127034 methods).

You can transform readily this CQL query into a CQL constraint to ensure that no methods exceed 200 IL instructions. The idea is that NDepend gets integrated into your NAnt or MSBuild process and that such a constraint emits a warning when it is not satisfied:

WARN IF Count > 0 IN SELECT METHODS 
WHERE NbILinstructions > 200 ORDER BY NbILinstructions

In a real-world environment, there are often exceptions (like automatically generated methods which are often very big) and you need to allow a few particular methods to exceed this threshold without being bothered by our previous constraint. The CQL language offers numerous features allowing you to deal with such exceptions. For example, all generated methods might contain the word "Generated" in their names:

WARN IF Count > 0 IN SELECT METHODS 
WHERE  NbILinstructions > 200 AND !NameLike "Generated" ORDER BY NbILinstructions DESC

Or maybe, all generated methods are in dedicated assemblies, namespaces or types:

WARN IF Count > 0 IN SELECT METHODS 
OUT OF NAMESPACES "MyApp.Generated1","MyApp.Generated2" 
WHERE NbILinstructions > 200 ORDER BY NbILinstructions DESC

Or maybe, you prefer to mention each one explicitly:

WARN IF Count > 0 IN SELECT METHODS 
WHERE NbILinstructions > 200 AND !(NameIs "Method1(int32)" OR FullNameIs "MyApp.MyType.Method2(String)" ) 
ORDER BY NbILinstructions DESC

You can also mix all these features in the same constraint:

WARN IF Count > 0 IN SELECT METHODS 
OUT OF NAMESPACES "MyApp.Generated1","MyApp.Generated2" 
WHERE NbILinstructions > 200 AND !(NameIs "Method1(int32)" OR FullNameIs "MyApp.MyType.Method2(String)" ) 
AND !NameLike "Generated" ORDER BY NbILinstructions DESC

CQL and code metrics

Here are some examples of CQL queries based on code metrics:

SELECT TOP 5 METHODS WHERE IsPublic ORDER BY NbILinstructions DESC
Name # IL instructions Full Name
GetClipboardContent() 1911 System.Windows.Forms.DataGridView.GetClipboardContent()
DrawBorder(Graphics,Rectangle,Color,int32,ButtonBorderStyle,
Color,int32,ButtonBorderStyle,Color,int32,ButtonBorderStyle,
Color,int32,ButtonBorderStyle)
1354 System.Windows.Forms.ControlPaint.DrawBorder(
Graphics,Rectangle,Color,int32,ButtonBorderStyle,Color,
int32,ButtonBorderStyle,Color,int32,ButtonBorderStyle,
Color,int32,ButtonBorderStyle)
GetinheritedStyle(DataGridViewCellStyle,int32,Boolean) 1116 System.Windows.Forms.DataGridViewCell.GetinheritedStyle(
DataGridViewCellStyle,int32,Boolean)
BindToMethod(BindingFlags,MethodBase[],Object[]&,
ParameterModifier[],Cultureinfo,String[],Object&)
1063 System.DefaultBinder.BindToMethod(BindingFlags,
MethodBase[],Object[]&,ParameterModifier[],Cultureinfo,
String[],Object&)
HitTest(int32,int32) 1031 System.Windows.Forms.DataGridView.HitTest(int32,int32)


SELECT TOP 5 METHODS WHERE IsPublic ORDER BY NbParameters DESC
Name # Parameters Full Name
GetAttributes(int32,String,String&,String,String&,
String,String&,String,String&, String,String&,String,
String&,String,String&,String, String&,String,String&,
String,String&)
21 System.Xml.Xsl.Xslt.Xsltinput.GetAttributes(int32,String,String&,String,
String&,String,String&,String,String&,String,String&,String,String&,String,
String&,String,String&,String,String&,String,String&)
GetAttributes(int32,String,String&,String,String&,
String,String&,String,String&,String,
String&,String,String&,String,String&,
String,String&,String,String&)
19 System.Xml.Xsl.Xslt.Xsltinput.GetAttributes(int32,String,String&,String,
String&,String,String&,String,String&,String,String&,String,String&,String,
String&,String,String&,String,String&)
.ctor(Byte,Byte,int32,Boolean,Boolean,Boolean,Byte,
Byte,String,Type,Type, SqlDbType,DbType,Byte)
14 System.Data.SqlClient.MetaType..ctor(Byte,Byte,int32,Boolean,Boolean,
Boolean,Byte,Byte,String,Type,Type,SqlDbType,DbType,Byte)

DrawBorder(Graphics,Rectangle,Color,int32,
ButtonBorderStyle,Color,int32, ButtonBorderStyle,Color,int32,ButtonBorderStyle,
Color,int32,ButtonBorderStyle)

14 System.Windows.Forms.ControlPaint.DrawBorder(Graphics,Rectangle,Color,
int32,ButtonBorderStyle,Color,int32,ButtonBorderStyle,Color,int32,
ButtonBorderStyle,Color,int32,ButtonBorderStyle)
.ctor(String,SqlDbType,int32,ParameterDirection,
Byte,Byte,String,DataRowVersion,
Boolean,Object,String,String,String)
13 System.Data.SqlClient.SqlParameter..ctor(String,SqlDbType,int32,
ParameterDirection,Byte,Byte,String,DataRowVersion,Boolean,
Object,String,String,String)


SELECT TOP 5 TYPES WHERE IsPublic ORDER BY NbILinstructions DESC
Name # IL instructions Full Name
DataGridView 67328 System.Windows.Forms.DataGridView
BrowserCapabilitiesFactory 45798 System.Web.Configuration.BrowserCapabilitiesFactory
Control 18456 System.Windows.Forms.Control
DataGrid 15799 System.Windows.Forms.DataGrid
DataTable 13597 System.Data.DataTable


SELECT TOP 5 TYPES WHERE IsPublic ORDER BY NbMethods DESC
Name # Methods Full Name
BrowserCapabilitiesFactory 1606 System.Web.Configuration.BrowserCapabilitiesFactory
DataGridView 1053 System.Windows.Forms.DataGridView
Control 869 System.Windows.Forms.Control
DataGrid 421 System.Windows.Forms.DataGrid
Form 349 System.Windows.Forms.Form


SELECT TOP 5 TYPES WHERE !IsEnumeration AND IsPublic ORDER BY NbFields DESC
Name # Fields Full Name
DataGridView 322 System.Windows.Forms.DataGridView
OpCodes 226 System.Reflection.Emit.OpCodes
HttpCapabilitiesBase 224 System.Web.Configuration.HttpCapabilitiesBase
Control 205 System.Windows.Forms.Control
Page 143 System.Web.UI.Page


SELECT TOP 5 TYPES WHERE IsPublic ORDER BY SizeOfInst DESC
Name Size of instance Full Name
DataGridView 556 System.Windows.Forms.DataGridView
ToolStripDropDownMenu 478 System.Windows.Forms.ToolStripDropDownMenu
ContextMenuStrip 478 System.Windows.Forms.ContextMenuStrip
PropertyGrid 419 System.Windows.Forms.PropertyGrid
DataGrid 408 System.Windows.Forms.DataGrid

Here is the definition of the SizeOfInst (size of instances) metric: The size of instances of an instance field is defined as the size, in bytes, of instances of its type. The size of instance of a static field is equal to 0. The size of instances of a class or a structure is defined as the sum of size of instances of its fields plus the size of instances of its base class. Fields of reference types (class, interface, delegate...) always count for 4 bytes while the footprint of fields of value types (structure, int, byte, double...) might vary. Size of instances of an enumeration is equal to the size of instances of the underlying numeric primitive type. It is computed from the value__ instance field (all enumerations have such a field when compiled in IL). Size of instances of generic types might be erroneous because we can't statically know the footprint of parameter types (except when they have the class constraint).


SELECT TOP 5 TYPES OUT OF ASSEMBLIES "System.Windows.Forms","System.Web" WHERE IsPublic ORDER BY SizeOfInst DESC
Name Size of instance Full Name
QueuePathDialog 373 System.Messaging.Design.QueuePathDialog
XmlSerializationReader 355 System.Xml.Serialization.XmlSerializationReader
HttpListenerResponse 321 System.Net.HttpListenerResponse
DataTable 265 System.Data.DataTable
HttpWebRequest 236 System.Net.HttpWebRequest


SELECT TOP 5 TYPES ORDER BY TypeCa DESC
Name Afferent coupling at type level (TypeCa) Full Name
Void 11940 System.Void
String 11786 System.String
int32 11710 System.int32
Boolean 11687 System.Boolean
Object 11670 System.Object

Here is the definition of the TypeCa (afferent coupling) metric: The Afferent Coupling for a particular type is the number of types that depends directly on it.


SELECT TOP 5 TYPES ORDER BY TypeCe DESC
Name Efferent coupling at type level (TypeCe) Full Name
DataGridView 377 System.Windows.Forms.DataGridView
ListView 313 System.Windows.Forms.ListView
Control 299 System.Windows.Forms.Control
PropertyGrid 274 System.Windows.Forms.PropertyGrid
AxHost 270 System.Windows.Forms.AxHost

Here is the definition of the TypeCe (efferent coupling) metric: The Efferent Coupling for a particular type is the number of types it directly depends on.


WARN IF Count > 0 IN SELECT TOP 10 TYPES 
WHERE LCOMHS > 1.0 AND NbFields > 10 AND NbMethods > 10 ORDER BY LCOMHS DESC
Name LCOM
Henderson-Sellers
(LCOMHS)
# Fields # Methods Full Name
HealthMonitoringSection 1.1 11 12 System.Web.Configuration.HealthMonitoringSection
ChineseLunisolarCalendar 1.1 12 16 System.Globalization.ChineseLunisolarCalendar
KoreanLunisolarCalendar 1.1 12 16 System.Globalization.KoreanLunisolarCalendar
RuleSettings 1 13 22 System.Web.Configuration.RuleSettings
OutputCacheProfile 1 11 24 System.Web.Configuration.OutputCacheProfile
RoleManagerSection 1 14 28 System.Web.Configuration.RoleManagerSection
FormsAuthenticationConfiguration 1 14 28 System.Web.Configuration.FormsAuthenticationConfiguration
ISAPIWorkerRequestinProc 1 45 23 System.Web.Hosting.ISAPIWorkerRequestinProc
OutputCacheSection 1 12 11 System.Web.Configuration.OutputCacheSection
PersianCalendar 1 17 34 System.Globalization.PersianCalendar

Here is the definition of the LCOMHS (Lack Of Cohesion Methods Henderson-Sellers) metric: The Efferent Coupling for a particular type is the number of types it directly depends on. The single responsibility principle states that a class should have more than one reason to change. Such a class is said to be cohesive. A high LCOM value generally pinpoints a poorly cohesive class. There are several LCOM metrics. The LCOM takes its values in the range [0-1]. The LCOM HS (HS stands for Henderson-Sellers) takes its values in the range [0-2]. A LCOM HS value highest than 1 should be considered alarming. Here are algorithms used by NDepend to compute LCOM metrics:

  • LCOM = (1-sum(MF)/M*F)
  • LCOM HS = (M- sum(MF)/F)(M-1)
  • Where:

  • M is the number of methods in class (both static and instance methods are counted, it includes also constructors, properties getters/setters, events add/remove methods).
  • F is the number of instance fields in the class.
  • MF is the number of methods of the class accessing a particular instance field.
  • Sum(MF) ids the sum of MF over all instance fields of the class.


  • SELECT TOP 5 NAMESPACES ORDER BY NbTypes DESC
    Name # Types Full Name
    System.Windows.Forms 1474 System.Windows.Forms
    System.Web.UI.WebControls 483 System.Web.UI.WebControls
    System.Net 445 System.Net
    System.Xml.Schema 298 System.Xml.Schema
    System 280 System


    SELECT TOP 5 TYPES WHERE IsPublic ORDER BY DepthOfInheritance DESC
    Name Depth of inheritance Full Name
    ContextMenuStrip 8 System.Windows.Forms.ContextMenuStrip
    CatalogZone 7 System.Web.UI.WebControls.WebParts.CatalogZone
    ToolStripOverflow 7 System.Windows.Forms.ToolStripOverflow
    EditorZone 7 System.Web.UI.WebControls.WebParts.EditorZone
    QueuePathDialog 7 System.Messaging.Design.QueuePathDialog


    SELECT TOP 5 TYPES OUT OF ASSEMBLIES "System.Windows.Forms","System.Web" WHERE IsPublic ORDER BY DepthOfInheritance DESC
    Name Depth of inheritance Full Name
    QueuePathDialog 7 System.Messaging.Design.QueuePathDialog
    HttpGetClientProtocol 6 System.Web.Services.Protocols.HttpGetClientProtocol
    HttpPostClientProtocol 6 System.Web.Services.Protocols.HttpPostClientProtocol
    OleDbPermissionAttribute 5 System.Data.OleDb.OleDbPermissionAttribute
    IsolatedStorageFilePermissionAttribute 5 System.Security.Permissions.IsolatedStorageFilePermissionAttribute

    CQL and naming rules

    Thanks to the NameLike "regex" CQL expression, you can write your own naming constraints. For example:

    WARN IF Count > 0 IN SELECT TOP 5 FIELDS WHERE NameLike "^m_" AND IsStatic
    Name Size of instance Full Name
    m_bitsPerByte 0 System.Security.Cryptography.MACTripleDES.m_bitsPerByte
    m_format 0 System.Security.Cryptography.X509Certificates.X509Certificate.m_format
    m_appTrustManager 0 System.Security.Policy.ApplicationSecurityManager.m_appTrustManager
    m_illegalCharacters 0 System.Security.Permissions.FileIOPermission.m_illegalCharacters
    m_strAllFiles 0 System.Security.Permissions.FileIOAccess.m_strAllFiles
    WARN IF Count > 0 IN SELECT FIELDS WHERE NameLike "^s_" AND !IsStatic
    Name Size of instance Full Name
    s_oneTimeinit 1 System.Web.SessionState.SessionStateModule.s_oneTimeinit
    s_internalSyncObject 4 System.Security.Policy.HashMembershipCondition.s_internalSyncObject
    WARN IF Count > 0 IN SELECT TOP 10 TYPES WHERE IsInterface AND !NameLike "^I" AND !IsNested AND IsPublic
    Name # IL instructions Full Name
    _AppDomain 0 System._AppDomain
    _Module 0 System.Runtime.interopServices._Module
    _Propertyinfo 0 System.Runtime.interopServices._Propertyinfo
    _Fieldinfo 0 System.Runtime.interopServices._Fieldinfo
    _Constructorinfo 0 System.Runtime.interopServices._Constructorinfo
    _Eventinfo 0 System.Runtime.interopServices._Eventinfo
    _Parameterinfo 0 System.Runtime.interopServices._Parameterinfo
    _Methodinfo 0 System.Runtime.interopServices._Methodinfo
    _AssemblyName 0 System.Runtime.interopServices._AssemblyName
    _Assembly 0 System.Runtime.interopServices._Assembly

    CQL and the graph of dependencies

    Suppose that you need to know which methods can potentially trigger the call of a particular methods, such as a Split() overload of the System.String class. CQL answer this need thanks to the IsCalling keyword:

    SELECT METHODS WHERE IsCalling "System.String.Split(Char[],int32,StringSplitOptions)" ORDER BY DepthOfIsCalling, NbILinstructions DESC
    Name DepthOfIsUsing
    '"System.String.
    Split(
    Char[],int32,
    StringSplitOptions)"'
    # IL instructions Full Name
    Split(Char[],int32,StringSplitOptions) 0 80 System.String.Split(Char[],int32,StringSplitOptions)
    Split(String[],int32,StringSplitOptions) 1 97 System.String.Split(String[],int32,StringSplitOptions)
    Split(Char[],int32) 1 6 System.String.Split(Char[],int32)
    Split(Char[]) 1 6 System.String.Split(Char[])
    Split(Char[],StringSplitOptions) 1 6 System.String.Split(Char[],StringSplitOptions)
    ResolveWsdlMethodinfo(WsdlParser+WsdlBinding) 2 770 System.Runtime.Remoting.MetadataServices.
    WsdlParser.ResolveWsdlMethodinfo(
    WsdlParser+WsdlBinding)
    Create(String) 2 575 System.Xml.Xsl.Runtime.XmlCollation.Create(String)
    get_MiscSectionContent() 2 571 System.Web.DynamicCompileErrorFormatter.
    get_MiscSectionContent()
    initOutputCache(OutputCacheParameters) 2 513 System.Web.UI.Page.
    initOutputCache(OutputCacheParameters)
    ProcessRequest(HttpContext) 2 495 System.Web.Handlers.AssemblyResourceLoader.
    ProcessRequest(HttpContext)
    OnEnter(Object,EventArgs) 2 452 System.Web.Caching.OutputCacheModule.
    OnEnter(Object,EventArgs)
    Read33_XmlSchemaSimpleTypeUnion
    (Boolean,Boolean)
    2 373 System.Web.Services.Description.
    ServiceDescriptionSerializationReader.
    Read33_XmlSchemaSimpleTypeUnion(Boolean,Boolean)
    GetDefaultValueArguments(PrimitiveMapping,
    Object, CodeExpression&)
    2 372 System.Xml.Serialization.XmlCodeExporter.
    GetDefaultValueArguments(PrimitiveMapping,
    Object,CodeExpression&)
    intersect(EndpointPermission) 2 344 System.Net.EndpointPermission.
    intersect(EndpointPermission)
    RaisePostBackEvent(String) 2 316 System.Web.UI.WebControls.WebParts.
    WebPartZoneBase.RaisePostBackEvent(String)
    ...(47869 methods selected)

    The CQL query above allows to see that 37.7% (47869 on 127034 with a maximum DepthOfIsCalling equal to 30) of the methods of the .NET framework 2.0 can potentially trigger the call of this overload of the Split() method of the System.String class.

    You might be more interested by knowing just which methods call this method directly:

    SELECT METHODS WHERE DepthOfIsCalling "System.String.Split(Char[],int32,StringSplitOptions)" == 1 ORDER BY DepthOfIsCalling
    Name DepthOfIsUsing
    '"System.String.
    Split(Char[],int32,
    StringSplitOptions)"'
    Full Name
    Split(Char[],StringSplitOptions) 1 System.String.Split(Char[],StringSplitOptions)
    Split(Char[],int32) 1 System.String.Split(Char[],int32)
    Split(String[],int32,StringSplitOptions) 1 System.String.Split(String[],int32,StringSplitOptions)
    Split(Char[]) 1 System.String.Split(Char[])

    On the opposite, you can use the keywords IsCalledBy and DepthOfIsCalledBy:

    SELECT METHODS WHERE IsCalledBy "System.String.Split(Char[],int32,StringSplitOptions)" 
    ORDER BY DepthOfIsCalledBy 
    Name DepthOfIsUsedBy
    '"System.String.
    Split(Char[],int32,
    StringSplitOptions)"'
    Full Name
    Split(Char[],int32,StringSplitOptions) 0 System.String.Split(Char[],int32,StringSplitOptions)
    internalSplitKeepEmptyEntries(
    int32[],int32[],int32,int32)
    1 System.String.internalSplitKeepEmptyEntries(int32[],int32[],int32,int32)
    .ctor(String,String) 1 System.ArgumentOutOfRangeException..ctor(String,String)
    get_Length() 1 System.String.get_Length()
    GetResourceString(String) 1 System.Environment.GetResourceString(String)
    internalSplitOmitEmptyEntries(int32[],
    int32[],int32,int32)
    1 System.String.internalSplitOmitEmptyEntries(int32[],
    int32[],int32,int32)
    MakeSeparatorList(Char[],int32[]&) 1 System.String.MakeSeparatorList(Char[],int32[]&)
    GetResourceString(String,Object[]) 1 System.Environment.GetResourceString(String,Object[])
    .ctor(String) 1 System.ArgumentException..ctor(String)
    GetResourceFromDefault(String) 2 System.Environment.GetResourceFromDefault(String)
    Substring(int32) 2 System.String.Substring(int32)
    .ctor(String) 2 System.SystemException..ctor(String)
    .ctor(String,String) 2 System.ArgumentException..ctor(String,String)
    SetErrorCode(int32) 2 System.Exception.SetErrorCode(int32)
    Format(IFormatProvider,String,Object[]) 2 System.String.Format(IFormatProvider,String,Object[])
    IsWhiteSpace(Char) 2 System.Char.IsWhiteSpace(Char)
    get_CurrentCulture() 2 System.Globalization.Cultureinfo.get_CurrentCulture()
    Substring(int32,int32) 2 System.String.Substring(int32,int32)
    .ctor(String) 3 System.ArgumentNullException..ctor(String)
    internalSubStringWithChecks(
    int32,int32,Boolean)
    3 System.String.internalSubStringWithChecks(int32,int32,Boolean)
    ...(744 methods selected)

    In the same spirit the CQL language provides the keywords IsUsing, DepthOfIsUsing IsUsedBy DepthOfIsUsedBy usable in the assemblies, namespaces or types context. The keywords CreateA and DepthOfCreateA can also be used to know which method can be responsible for calling the constructor of a type:

    SELECT METHODS WHERE DepthOfCreateA "System.Diagnostics.Process" < 3 ORDER BY DepthOfCreateA
    Name DepthOfCreateA
    "System.Diagnostics.
    Process"
    Full Name
    .ctor(String,Boolean,int32,Processinfo) 0 System.Diagnostics.Process..ctor(String,Boolean,int32,Processinfo)
    .ctor() 0 System.Diagnostics.Process..ctor()
    GetProcessById(int32,String) 1 System.Diagnostics.Process.GetProcessById(int32,String)
    GetProcesses(String) 1 System.Diagnostics.Process.GetProcesses(String)
    GetCurrentProcess() 1 System.Diagnostics.Process.GetCurrentProcess()
    Start(ProcessStartinfo) 1 System.Diagnostics.Process.Start(ProcessStartinfo)
    GetProcessById(int32) 2 System.Diagnostics.Process.GetProcessById(int32)
    Start(String,String) 2 System.Diagnostics.Process.Start(String,String)
    Restart() 2 System.Windows.Forms.Application.Restart()
    initProcessinfo() 2 System.Diagnostics.TraceEventCache.initProcessinfo()
    get_ProcessName() 2 System.Transactions.Diagnostics.DiagnosticTrace.get_ProcessName()
    get_ProcessId() 2 System.Transactions.Diagnostics.DiagnosticTrace.get_ProcessId()
    LoadPerfCounterDll() 2 System.Diagnostics.CounterSampleCalculator.LoadPerfCounterDll()
    Start(String) 2 System.Diagnostics.Process.Start(String)
    GetProcesses() 2 System.Diagnostics.Process.GetProcesses()
    RegisterFiles(String,Boolean) 2 System.Diagnostics.PerformanceCounterLib.RegisterFiles(String,Boolean)
    .cctor() 2 System.Web.Management.WebProcessStatistics..cctor()
    GetDefaultAppName() 2 System.Web.Util.SecUtility.GetDefaultAppName()
    GetDataDirectory() 2 System.Web.DataAccess.SqlConnectionHelper.GetDataDirectory()
    initApp() 2 System.Web.Security.AuthorizationStoreRoleProvider.initApp()
    Update() 2 System.Web.Management.WebProcessStatistics.Update()
    .ctor() 2 System.Web.Management.WebProcessStatistics..ctor()
    RuntimeDatainitialize() 2 System.Web.Configuration.MachineKeySection.RuntimeDatainitialize()
    Start(String,String,String,SecureString,String) 2 System.Diagnostics.Process.Start(String,String,String,SecureString,String)
    Start(String,String,SecureString,String) 2 System.Diagnostics.Process.Start(String,String,SecureString,String)
    GetinstanceName() 2 System.Net.NetworkingPerfCounters.GetinstanceName()
    get_Identifier() 2 System.Net.Networkinformation.IcmpPacket.get_Identifier()
    GetProcessesByName(String,String) 2 System.Diagnostics.Process.GetProcessesByName(String,String)

    These keywords can be used in custom CQL constraints to ensure that some kind of encapsulation will be respected. For example you might wish to restrict the possibility of using the type "System.Xml.XmlChildNodes" only to certain namespace.

    WARN IF Count > 0 IN SELECT TYPES OUT OF NAMESPACES "System.Xml" WHERE DepthOfIsUsing "System.Xml.XmlChildNodes" == 1

    Or you might wish to restrict the possibility of creating instance of "System.Xml.XmlLoader" only to certain namespaces.

    WARN IF Count > 0 IN SELECT METHODS OUT OF NAMESPACES "System.Xml"
    WHERE DepthOfCreateA "System.Xml.XmlLoader" == 1 ORDER BY DepthOfCreateA

    Or you want to assert that the assembly System.Web.Services will never be referenced by another assembly than System.Web:

    WARN IF Count > 0 IN SELECT ASSEMBLIES WHERE !NameIs "System.Web" AND DepthOfIsUsing "System.Web.Services" == 1

    CQL and the graph of inheritance

    Let's precise that the CQL language provides also the keywords Implements DeriveFrom and DepthOfDeriveFrom

    SELECT TYPES WHERE DepthOfDeriveFrom "System.Windows.Forms.Control" == 1
    Name DepthOfDeriveFrom 'Control' Full Name
    DataGridView 1 System.Windows.Forms.DataGridView
    DataGrid 1 System.Windows.Forms.DataGrid
    ListView 1 System.Windows.Forms.ListView
    AxHost 1 System.Windows.Forms.AxHost
    TreeView 1 System.Windows.Forms.TreeView
    MonthCalendar 1 System.Windows.Forms.MonthCalendar
    TabControl 1 System.Windows.Forms.TabControl
    TextBoxBase 1 System.Windows.Forms.TextBoxBase
    ToolBar 1 System.Windows.Forms.ToolBar
    ScrollableControl 1 System.Windows.Forms.ScrollableControl
    Label 1 System.Windows.Forms.Label
    WebBrowserBase 1 System.Windows.Forms.WebBrowserBase
    DateTimePicker 1 System.Windows.Forms.DateTimePicker
    PrintPreviewControl 1 System.Windows.Forms.PrintPreviewControl
    PictureBox 1 System.Windows.Forms.PictureBox
    StatusBar 1 System.Windows.Forms.StatusBar
    ButtonBase 1 System.Windows.Forms.ButtonBase
    Splitter 1 System.Windows.Forms.Splitter
    TrackBar 1 System.Windows.Forms.TrackBar
    ListControl 1 System.Windows.Forms.ListControl
    GroupBox 1 System.Windows.Forms.GroupBox
    ScrollBar 1 System.Windows.Forms.ScrollBar
    ProgressBar 1 System.Windows.Forms.ProgressBar
    MdiClient 1 System.Windows.Forms.MdiClient
    UpDownBase+UpDownButtons 1 System.Windows.Forms.UpDownBase+UpDownButtons
    StatusStrip+RightToLeftLayoutGrip 1 System.Windows.Forms.StatusStrip+RightToLeftLayoutGrip
    PropertyGrid+SnappableControl 1 System.Windows.Forms.PropertyGrid+SnappableControl
    Application+MarshalingControl 1 System.Windows.Forms.Application+MarshalingControl
    SendKeys+SKWindow 1 System.Windows.Forms.SendKeys+SKWindow
    PropertyGridView 1 System.Windows.Forms.PropertyGridinternal.PropertyGridView
    GridToolTip 1 System.Windows.Forms.PropertyGridinternal.GridToolTip
    ContentAlignmentEditor+ContentUI 1 System.Drawing.Design.ContentAlignmentEditor+ContentUI
    ColorEditor+ColorUI 1 System.Drawing.Design.ColorEditor+ColorUI
    ColorEditor+ColorPalette 1 System.Drawing.Design.ColorEditor+ColorPalette
    SELECT TYPES WHERE Implement "System.ComponentModel.IComponent"
    Name # IL instructions Full Name
    DataGridView 67328 System.Windows.Forms.DataGridView
    Control 18456 System.Windows.Forms.Control
    DataGrid 15799 System.Windows.Forms.DataGrid
    DataTable 13597 System.Data.DataTable
    ListView 10875 System.Windows.Forms.ListView
    Form 10234 System.Windows.Forms.Form
    PropertyGridView 9679 System.Windows.Forms.PropertyGridinternal.PropertyGridView
    PropertyGrid 8122 System.Windows.Forms.PropertyGrid
    WebPartManager 7880 System.Web.UI.WebControls.WebParts.WebPartManager
    GridView 7059 System.Web.UI.WebControls.GridView
    ToolStrip 6762 System.Windows.Forms.ToolStrip
    Page 6712 System.Web.UI.Page
    TreeView 6396 System.Web.UI.WebControls.TreeView
    DataSet 6112 System.Data.DataSet
    AxHost 5888 System.Windows.Forms.AxHost
    DetailsView 5835 System.Web.UI.WebControls.DetailsView
    RichTextBox 5684 System.Windows.Forms.RichTextBox
    SqlCommand 5482 System.Data.SqlClient.SqlCommand
    ComboBox 5421 System.Windows.Forms.ComboBox
    TreeView 5204 System.Windows.Forms.TreeView
    Menu 5034 System.Web.UI.WebControls.Menu
    MessageQueue 4899 System.Messaging.MessageQueue
    EventLog 4709 System.Diagnostics.EventLog
    WebClient 4687 System.Net.WebClient
    ConnectionsZone 4543 System.Web.UI.WebControls.WebParts.ConnectionsZone
    CreateUserWizard 4266 System.Web.UI.WebControls.CreateUserWizard
    FormView 4250 System.Web.UI.WebControls.FormView
    ToolStripItem 4192 System.Windows.Forms.ToolStripItem
    ToolTip 4072 System.Windows.Forms.ToolTip
    SplitContainer 4040 System.Windows.Forms.SplitContainer
    ... (443 types selected)

    This article doesn't illustrate all capabilities of the CQL language. You can learn this language by reading CQL 1.0 specification and you can use it on your own .NET application by downloading NDepend 2.0.

    Patrick Smacchia is a .NET MVP involved in software development for over 15 years. He is the author of Practical .NET2 and C#2, a .NET book conceived from real world experience with 647 compilable code listings. After graduating in mathematics and computer science, he has worked on software in a variety of fields including stock exchange at Société Générale, airline ticket reservation system at Amadeus as well as a satellite base station at Alcatel. He's currently a software consultant and trainer on .NET technologies as well as the author of the freeware NDepend which provides numerous metrics and caveats on any compiled .NET application.

    Article Discussion: