.NN #16: Cool Attributes and A Preprocessor Directive or Two

For this .NET Nugget I’d like to touch on a few cool code Attributes and a couple of helpful PreProcessor Directive in C#.

The first attribute I’d like to bring up is Obsolete (no, it isn’t obsolete, but it tells other developers that some code object is Obsolete).  This is a really handy attribute you use during development to keep track of dying or dead methods.  You can use this attribute to mark up a method as Obsolete, informing other developers that at some point in the future this method will be deprecated.  Here is an example in VB.NET:

<Obsolete("Please call 'SomeBrandNewMethod' instead.", False)> _
Private Sub SomeDyingMethod()

In the above VB.NET method I’ve used the Obsolete attribute to inform other developers that they should call SomeBrandNewMethod instead of SomeDyingMethod.  The boolean parameter to the attribute indicates if the compiler should cause an error if this method is referenced in other code (false is the default, which generates a compiler warning).  Once you add this for each reference of this method you’ll get a compile warning or error.  It will show you exactly where this method is called.  The attribute works across C# code calling VB.NET and vice versa as well as it is a .NET Framework attribute and not language specific (though the language compiler certainly needs to support it).  You can use this on a class, struct, enum, constructor, event, method, property, field, interface or delegate.

The scenario I’ve used this for in the past is to mark a code object as obsolete, provide a message and then indicate it should provide a warning.  Then when I’m ready to really remove the item I change the boolean over to a true.  This means that the other developers will not be able to compile since calling this method will now generate a compile error.  This works well in a development effort when you are phasing out methods with other developers on the team; however, I’d say it would probably not do well to use this with the “True” option on a public method exposed on your shrink wrapped product, at least not without a lot of warning to your customers.

The next attribute I want to talk about is DebuggerDisplay.  This attribute is used to tell the IDE how to display a code member in the debugger.  This isn’t a custom debug visualizer but rather a way to format the display of the object in the standard debugger.  Take a look at the following C# class definition (works in VB.NET just the same, though some of the string formatting options can be language specific):

[DebuggerDisplay("Person-ID:{Id}, Name:{Name}")]
public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
 
 
    public Person()
    {
 
    }
}

In the attribute I indicate that I want to describe the object via it’s Id and Name properties.  If you create an instance of this and view it in the debugger (either with the quick watch, watch window, or simply hover over the instance variable while in the debug mode you see the formatted description.

image

 image

As you can see this can come in handy by adding one or two really identifying properties to the Debugger Display so that you can quickly see the object instance for what it is without having to expand the instance out.  This works really well if you have a LOT of properties and fields on the object.

The attribute also has some more parameters that let you change the text in the name and type columns from the watch window, as well as how to target a particular type with the attribute if the attribute is used at the Assembly level.  You can use this attribute on a class, struct, delegate, enum, field, property or assembly. 

Next up is not an attribute but a C# Preprocessor Directive.  Preprocessor directives are used by the compiler and include things like #if for conditional compiling.  The directives I want to talk about are #warning and #pragma.

#warning is used to let the developer code a compiler warning directly into the code.  I started using this a lot for my own work when I realized the C#’s implementation of the VS.NET Task List is horribly broken.  In VB.NET if there is a ‘TODO comment anywhere in the solution it will appear on the Task List.  In C# a //TODO will only appear in the Task List if the file it is in happens to be open!  That’s just plain ridiculous.  I used to use Find in Files looking for //TODO to find all my TODOs in C#.  Now I can add a #warning instead to leave myself, or other developers, messages.

#warning Talk to Bob RE: what type of DebuggerDisplay do we want for Person
public Person()
{
 
}

NOTE: The warning must all appear on one line (the display above my wrap due to the nature of HTML, but in code it has to be on one line).  Also, if you use CodeRush you may experience some template expansion as you type your warnings….very annoying.  I usually start out with //#warning and then remove the comment moniker after I’m done typing the statement.  I need to add that issue to the DevExpress KB [EDIT: Someone else already has; hopefully it will get fixed sometime soon].

The above code will cause a compiler warning when I compile.  I see these when I do builds and it catches my attention.  I don’t like checking in code that has compiler warnings and I’ve gotten into the habit of compiling and running unit tests prior to every check in  so for me this is a way to ensure I tidy up my loose ends.  I can also use this to indicate something like:

public List<Person> GetPeople()
{
#warning The GetPeople method is currently returning stubbed data!
}

This lets the other developer who is consuming my code that I’m sending him back stubbed data.  This isn’t a replacement for good ole fashion communication with that developer, but it’s a nice reminder. 

Now, before someone jumps me for using compiler warnings in the manner I’ve described understand that I rarely leave these in the code when I check things in.  If I do, it is because I really want to convey something to the other developers.  Usually I’m adding these as notes to myself to take care of something before I’m done with the code I’m working with.  Which I wouldn’t have to do if the Task List implementation in C# didn’t suck.

The #pragma directive is actually a family of directives.  This directive gives the compiler special instructions regarding the file in which the directive appears.  There are two #pragma directives in C# currently: warning (which I’ll cover) and checksum (which I won’t).

#pragma warning allows for you to inform the compiler to actually ignore a specific type of compiler warning (or all warnings) for a given file.  For example, the following code produces a level four compiler warning number 78:

image 

The warning is right, if you just glance at the code you’d think this was 251, not a 25 with a type of Long.  The compiler knows better, but the developer may not.  You could suppress this by using the following:

#pragma warning disable 78
static void Main(string[] args)
{
    var x = 25l;
}
#pragma warning restore

This tells the compiler to ignore warning 78, which happens to be the warning that was displayed above.  Note that I had to restore the warning list below the code to ensure that the warning was shown to me elsewhere.  By not indicating a number at the end of the the restore it will restore ALL warnings that may have been turned off for the file.  Also, if you put #pragma warning disable with no parameters it will disable all warnings for that file, or until warning(s) are restored.

The example above is a little contrived since I doubt anyone would argue that it’s a good idea to use the lowercase L like that.  Also, you could solve the issue above by declaring the variable with a type instead.  With that said you may indeed run across a warning that is being generated that you (and the other developers) have decided you can live with.  To keep the build free of warnings you can now use #pragma warning to get rid of that pesky warning.

In conclusion, this .Net Nugget is again about knowing your tools.  You may not agree with how I use the #warning or find use in the #pragma warning directives, but it is good that you now know what they are if you see them in code.  Do you have any attributes or directives that you use regularly that you think others should know about?  If so, leave a comment.