3/10/2010
LifeCycle Solutions - Home ( the software development blog )
 

<March 2010>
SunMonTueWedThuFriSat
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910

Subscribe to this feed:

RSS 2.0 | Atom 1.0 |CDF






Thursday, February 26, 2009

I've recently started playing with NDepend again.  This tool is a sort of data mining utility for your code.  Feed it your assemblies and it can tell you almost anything about them.  Using a custom query language with a SQL-like syntax, you can dig into all sorts of metrics and useful information. Out of the box, it ships with tons of useful queries for common code problems. 

I ran some of our code through it and got dinged on some of the code complexity queries:

 image

In this case, I had several huge methods that needed to be broken up and optimized.  Thanks to NDepend's prodding, I spent some time refactoring using ReSharper, and whittled those down to 0s:

image

That's helpful, but not nearly everything that NDepend can do.  For example, the code I'm testing with is involved in some multithreading, so with a little digging through the docs, I came up with this query to show all methods that change state (besides property and event setters), but don't use any locks:

WARN IF Count < 0 IN SELECT METHODS WHERE
!IsDirectlyUsing "System.Threading.Monitor" AND ( ChangesObjectState OR ChangesTypeState ) AND
!IsConstructor AND !IsClassConstructor AND !IsPropertySetter AND !IsEventAdder AND !IsEventRemover

This doesn't guarantee thread-safe code, but it definitely helps drill down to potential problem areas.

Another area I can see us using this in is to provide metrics for our customers.  Generally they are not concerned with things like Cyclomatic Complexity, but some simple numbers may be useful.  We use FinalBuilder, so it's possible we could run NDepend's console utility to roll the numbers into some XML and publish to their customer wiki.  For example, here's a quick query to spit out the total number of tests for a project:

SELECT METHODS FROM ASSEMBLIES "TestAssemblyName" WHERE HasAttribute "NUnit.Framework.TestAttribute"

Once we get a customer set up with these metrics, we'll post more about how to automate that.  Until then, go check out NDepend!

Posted by Daniel Root

Friday, July 11, 2008

I blogged about NArrange a while back, but there's a new version out that makes it even better.  It adds support for organizing code without adding #region directives, which I'm beginning to agree are evil.  The end result is a code file organized by member type and access level (ie fields, properties, private, public, etc), and then alphabetically.  It includes a configuration tool which lets you set up a configuration file with all of the rules you want it to follow.  To turn off #region directives open a configuration file in narrange-config, and change Formatting -> Regions -> Region Style to 'NoDirective'.

Posted by Daniel Root

Thursday, February 28, 2008

I've been experimenting with the idea of using System.Reflection.Emit to generate code from interfaces.  If you're not familiar with this namespace, it contains classes to generate compiled assemblies programmatically.   Regex uses this when compiling a regular expression for faster execution.  Instead of parsing the expression each time, it generates on-the-fly a class that can be used to execute the expression.  This namespace is also used by Mock Type frameworks such as RhinoMocks and TypeMock.  Given a base class or interface, these generate on-the-fly a class that can mimic the base enough to use when unit testing.  I'm sure there are other places this is used, but suffice it to say this is one of those obscure corners of the framework that us Morts rarely dig into.  So it's not surprising that I would run into an obscure error that stumped even Google.

I religiously studied the MSDN examples and wrote code to let me do something like this:

ICustomer concreteType = CodeGenerator<ICustomer>.GetInstance();

You can see how freaking awesome this could be for component development- I get, at runtime, a class that implements my interface, but I don't have to wire up goo like INotifyPropertyChanged, IDataError, property getter/setters, etc.   Depending on how my tinkering goes, more on the awesomeness later.  The error I was getting whenever I called typeBuilder.CreateType() was:

System.TypeLoadException: Method 'get_Id' in type 'Customer' from assembly 'TypeAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation

Except I WAS implementing 'get_Id'.  I copied the code to do it straight from MSDN! See:

MethodAttributes getSetAttributes = MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig;
var getMethodBuilder = typeBuilder.DefineMethod("get_" + propertyOnInterface.Name,
                                                getSetAttributes,
                                               propertyOnInterface.PropertyType,
                                               Type.EmptyTypes);

ILGenerator getMethodIl = getMethodBuilder.GetILGenerator();
getMethodIl.Emit(OpCodes.Ldarg_0);
getMethodIl.Emit(OpCodes.Ldfld, fieldBuilder);
getMethodIl.Emit(OpCodes.Ret);

propBuilder.SetGetMethod(getMethodBuilder);

Much googling turned up only a few unrelated posts.  After testing, I discovered that removing a call to 'AddInterfaceImplementation' fixed the issue- though the resulting class no longer implemented my interface.  Clearly the interface was generating some sort of code.  Some more digging, and I figured out the solution:

MethodAttributes getSetAttributes = MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig;| MethodAttributes.Virtual;

I could be flawed in my understanding here, but apparently, when adding an interface, .NET treats property get and set accessors sort of like abstract methods.  The code I was generating did implement 'get_Id', but did not mark it as overriding the interface's definition.  So, when creating the type, there was the interfaces' unfinished implementation of 'get_Id', and my completely unrelated 'get_Id'. 

So anyway, hopefully this post will make it into Google and help some other poor guy who, late one night, finds themselves turning over parts of the framework better left alone.

Posted by Daniel Root

Tuesday, February 20, 2007

One of the most useful tools for .NET development is Lutz Roeder's .NET Reflector, now in version 5.  It uses reflection to peek inside .NET assemblies and disassembles the IL back into C# or VB code for those times that you need an in-depth look into any .NET library.  Ever wonder what the code for System.String looked like? This release adds some really nice features:

  • Formatted code comments- doubles as a documentation browser for XML code-commented members
  • 'Expand members' in disassembler lets you see code for an entire class at once- previously you had to look at each method separately.
  • New analyzer features shows where classes are instantiated and exposed.
  • Search Google or MSDN for a member

In addition to helping you debug code, this is also a great learning tool.  What better way to understand framework development than to peek inside The Framework itself?

 

 

Posted by Daniel Root

Thursday, January 04, 2007

One of the reasons I prefer C# to VB.Net is for its brevity (not true in every case, but generally so).  Adding to the list of its syntax shortcuts, C# 2.0 introduces a new operator:  "??", dubbed the "null coalescing" operator.  For you SQLServer gurus out there, it is somewhat analagous to the COALESCE() function in Transact-SQL...it evaluates a single value and returns a second value if the first value is null (note, the type involved must be nullable).  

string
customerName=null;
string newCustomerName=null
;

// a long way of assigning newCustomerName
if (customerName == null
) {
   newCustomerName = "Empty"
; }
else
{
   newCustomerName = customerName;}

// a slightly shorter approach, using ?: "ternary operator"
newCustomerName = (customerName == null ? "Empty"
: customerName);

// much more concise, using the new ?? operator
newCustomerName = customerName ?? "Empty";

More information here
.

Posted by Brian Parks

© 2006 LifeCycle Solutions, LLC | All Rights Reserved