Blog Stats
  • Posts - 36
  • Articles - 0
  • Comments - 14
  • Trackbacks - 26

 

Tuesday, February 19, 2008

Debugging Attributes


Debugging Attributes

Have you ever been debugging code and examined the properties of an object? You get both the public properties and the private member variables!   Why is that?   Can this be avoided?

This seems to violate the principles of encapsulation.  It also complicates the process of examining the contents of an object at run time.   You don’t really want to have to look at all of the private member variables which by definition should be irrelevant to the task of debugging.

Fortunately the violations to encapsulation pose limited risk.   Even though the debugger will reveal hidden member variables, you still cannot access them in your code.

The confusing part is when you view an object’s detail and you have to filter through potentially twice as many variables as there are properties.

How do you remove this potential confusion in the objects that you create?   Enter the System.Diagnostics.DebugBrowsable attribute:

 Add a DebuggerBrowsable attribute to private member variables.

[System.Diagnostics.DebuggerBrowsable (System.Diagnostics.DebuggerBrowsableState.Never)]

Now these variables will not show up when you do a Quick Watch.   This will clean out the entries from the Quick Watch that you did not intend to be visible.

 

Another debugging frustration is stepping into methods or properties that you are no longer interested in.

Before c# 3.0 introduced implied properties, business entities could be particularly frustrating since they often included several very simple properties that you never had any need to step into.

I would often try to step into a method and have to step into several properties’ get accessors to pass their values as parameters.

Not only is this annoying, it is also potentially confusing and time consuming as the debug bounces around in irrelevant code as you are trying to figure out why an errant method is not behaving as you expect.

If only there was a way to instruct the debuger to ignore certain pieces of code …

Turns out that there is!

[System.Diagnostics.DebuggerHidden]  Causes the debugger to completely ignore the method.

[System.Diagnostics.DebuggerStepThrough] The debugger will not step into the method but will honor breakpoints in the method.

[System.Diagnostics.DebuggerNonUserCode] This attribute is similar to combining the first two attributes.   This is intended for generated code or designer code.  

Since these are attributes, you have to pay attention to the AttributeUsageAttribute.

DebuggerHidden and DebuggerNonUserCode can be tied to properties.    DebuggerStepThrough cannot.   It can be applied to methods though.   This means that it would have to be applied to the get and set individually for a property and not to the property as a whole.

The DebuggerStepThrough attribute causes some problems for CodeDom since there is no way to associate attributes with the individual methods that make up a property.  Use the DebuggerNonUserCodeAttribute instead.

 

Conditional Attribute


ConditionalAttribute

Certain methods especially in the System.Diagnostics namespace are decorated with ConditionalAttributes similar to this:

[Conditional("DEBUG")]

What is this all about?  The conditional attribute provides a nice way to have code be conditionally ignored.  Methods decorated with this attribute will not have their calls compiled to IL unless the condition is true.

In the attribute shown above, any calls to the method will not be made unless the symbol DEBUG is defined.

This creates some very intriguing possibilities.  Some of which might be nice and some of which might be pure evil.

Code like this:

#define TEST
using System;
public class MyClass
{                
   public static void Main()
   {
      #if (TEST)
         Console.WriteLine("TEST is defined");      
      #endif
   }
}


Can be rewritten to code like this:

 

    #define TEST
    public class MyClass
    {

        public static void Main()
        {
            TestWriteLine("Test is defined");
        }

        [System.Diagnostics.Conditional("TEST")]
        public static void TestWriteLine(string message)
        {
            Console.WriteLine(message);
        }
   }
Is this better?   Consider this, you would have to wrap every method call the “if test compiler directives” to achieve the same effect of adding the attribute to one place.

If you are only calling the method from one place, this probably doesn’t matter, but if you are potentially calling this method from throughout multiple enterprise applications, this is unrealistic.

The compiler directives can cause confusion by interrupt the logic flow.  

Here are some examples where this might be very useful and practical

Suppose you wanted to be able to support a build that could be used for code coverage testing without having to change code for a production build.

Suppose you wanted to have a build to support performance metrics without having to change any code.

Suppose you wanted to have a build to support debugging information without having to change production code.

Turns out, this last example is the whole reason this attribute was created.   This is how Debug.WriteLine works.    

Try this, sprinkle your code with calls System.Diagnostics.Debug.WriteLine(),  compile in debug mode, and examine the assembly with Reflector.   You will notice that all of your calls to WriteLine are there.   Now compile in release mode and examine the assembly with Reflector.   You didn’t change any code, but now none of the WriteLine statements are there.

We could create a method decorated with a conditional attribute requiring that the symbol CodeCoverage must be defined and then call this method at the start of every function.   Compile the code defining this symbol.   Now when we run through a round of testing, we will get a list of every method that was called.    Recompile the code without defining this symbol and the overhead of logging function calls goes away.

As is often the case, there are a couple of caveats.  This is not documented on MSDN anywhere that I have seen.   Any method decorated with this attribute must return void and cannot have output parameters.  This is intended to prevent the removal of this code from causing any unintended side effects.  You would not want to rely on a variable being initialized by a function that may or may not be called.  

How could you envision using such attributes?  

 

 
 

 

Copyright © Nick Harrison