Patrick Smacchia [MVP C#]

Sponsors

The Lounge

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
Attributes as a mean to partition code

 

On top of things Code Query Language users where asking for, was the condition HasAttribute. I am glad to announce that NDepend 2.7 supports CQL 1.6 that supports this condition for assemblies, types, methods and fields. This single condition opens the door to a wide range of interesting scenarios.

 

 

 

Attributes and generated code

 

You can use HasAttribute to segregate code. For example suppose that your generated methods are tagged by the attribute YourNamespace.YourGeneratedAttribute. You can now write very easily a CQL constraints to care about code quality of code non-generated such as:

 

WARN IF Count > 0 IN SELECT METHODS  WHERE

// Quality conditions

( NbLinesOfCode > 30 OR CyclomaticComplexity > 10 OR ILNestingDepth > 6 )

AND !HasAttribute "YourNamespace.YourGeneratedAttribute"

 

 

We could have define a NDepend.CQL.GeneratedAttribute attribute in the assembly NDepend.CQL.dll for example but we want to make NDepend non-intrusive to your code. In other words, we want to make sure that your code doesn’t mention anything about NDepend. This way, the attribute class YourNamespace.YourGeneratedAttribute is totally yours and you can reuse it in any way you want.

 

 

 

Tests attributes

 

You can also use the HasAttribute condition to identify test methods: 

 

SELECT METHODS WHERE HasAttribute "NUnit.Framework.TestAttribute"

 

More interestingly, you can write an astute query to get an estimation of which test methods is testing a particular method YourClass.YourMethod():

 

SELECT METHODS WHERE

HasAttribute "NUnit.Framework.TestAttribute" AND

IsUsing "YourClass.YourMethod()"

 

Notice that because the IsUsing condition returns the transitive closure of callees, this query will also match test methods that are using a method that is using YourClass.YourMethod().

 

 

 

Attribute to check for Immutability

 

The condition HasAttribute can also be used to make sure that some methods or some classes have some properties that can be statically verified by NDepend. This is like if NDepend was an extension of the C# compiler in charge to verify that some contracts are respected.

 

For example, I observed recently a great buzz about immutability in the .NET community. For more information on immutability in .NET and the various facilities offered by NDepend to handle it, please have a glance at this blog post: Immutable types: understand their benefits and use them. With the HasAttribute condition, it is easy to check for the immutability of your types with an ImmutableAttribute and the rule:

 

WARN IF Count > 0 IN SELECT TYPES WHERE

!IsImmutable AND HasAttribute "YourNamespace.ImmutableAttribute"

 

And as Greg Young noticed here, this is an idea that Microsoft is already harnessing because of the attribute ImmutableAttribute in the namespace Microsoft.Contracts in the assembly System.Core. The bad news is that Microsoft keeps all this internal. The good news is that with NDepend you can have it all and more Smile.

 

Interestingly enough, we check that the .NET Fx (including WPF, WCF…) doesn’t rely yet on the immutability attributes. Only the internal class System.Numeric.BigInteger is actually using it.

 

In the same spirit, you can check for method purity. I mean, you can make sure that a method won’t modify any state thanks to the CQL conditions ChangesObjectState and ChangesTypeState (a la const keyword in C++) :

 

WARN IF Count > 0 IN SELECT TYPES WHERE

(ChangesObjectState OR ChangesTypeState) AND

HasAttribute "YourNamespace.PureAttribute"

 

 

 

Attribute to check for tricky encapsulation scenario

 

The condition HasAttribute can also be a great help in the optimal encapsulation scenario. If you are using NDepend to make sure that your method and types are optimally encapsulated (i.e not declared as public when it could be declared as internal, protected or private for example) you certainly found out some cases where it is not applicable. For example, the Windows Form designer requires your control classes to be public in order to use them (actually there are plenty of cases where a type should be declared as public to be used by a tool or a framework). Imagine that your control is only used inside your assemblies, then NDepend will tell you that it should be declared as internal with the following rule:

 

WARN IF Count > 0 IN SELECT TYPES WHERE CouldBeInternal

 

A good solution to this problem is to tag your public control class with a CantBeInternalBecauseOfWindowsFormDesignerAttribute and to rewrite your constraint:

 

WARN IF Count > 0 IN SELECT TYPES WHERE

CouldBeInternal AND

!HasAttribute "YourNamespace.CanBeInternalBecauseOfWindowsFormDesignerAttribute"

 

This might sound awkward but think about it: Thanks to this attribute you’ve added a very useful active comment for future developers of your code: Beware! Make sure to not declare this type as internal!

 

 You could also add the condition…

!CouldBeInternal AND

HasAttribute "YourNamespace.CanBeInternalBecauseOfWindowsFormDesignerAttribute"

…to make sure that the attribute is only used where it should be.

 

 

And more...

 

Let's use the HasAttribute condition in any scenario you can imagine like:

 

  • Restricted use on a class:

WARN IF Count > 0 IN SELECT TYPES WHERE

IsDirectlyUsing "YourNamespace.Foo" AND

!HasAttribute "YourNamespace.AllowedToUseFooAttribute"

 

  • Restricted possibility on instantiating a class:

WARN IF Count > 0 IN SELECT TYPES WHERE

DepthOfCreateA "YourNamespace.Foo" == 1 AND

!HasAttribute "YourNamespace.AllowedToInstantiateFooAttribute"

 

  • Restricted use on assigning a field:

WARN IF Count > 0 IN SELECT TYPES WHERE

IsDirectlyWriting "YourNamespace.Foo.m_Field" == 1 AND

!HasAttribute "YourNamespace.AllowedToWriteFieldAttribute"

 

 

  • I don’t want that this code change (because it will be refactored for example):

WARN IF Count > 0 IN SELECT TYPES WHERE

CodeWasChanged AND

HasAttribute "YourNamespace.MustNotBeChangedAttribute"

 

 

  • Some assemblies should only contain interfaces (i.e, not a single lines of code):

WARN IF Count > 0 IN SELECT ASSEMBLIES WHERE

NbLinesOfCode > 0 AND 

HasAttribute "YourNamespace.OnlyInterfaceAssemblyAttribute"

 

 


Posted Fri, Mar 14 2008 8:54 PM by Patrick Smacchia

[Advertisement]

Comments

Christopher Bennage wrote re: Attributes as a mean to partition code
on Fri, Mar 14 2008 10:05 PM

I'm really finding these posts useful as I am trying to learn NDepend.Thanks!

Christopher Steen wrote Link Listing - March 18, 2008
on Wed, Mar 19 2008 7:11 AM

Link Listing - March 18, 2008

Christopher Steen wrote Link Listing - March 18, 2008
on Wed, Mar 19 2008 7:12 AM

AJAX ASP.NET AJAX callbacks to Web Methods in ASPX pages [Via: Frank Wang ] ASP.NET DotNetOpenId 0.1.2...

Add a Comment

(required)  
(optional)
(required)  
Remember Me?