KnowDotNet Visual Organizer

Attributes and Declarative Programming

The Death of Traditional Compiler Directives

by William Ryan
Print this Article Discuss in Forums

Since I've been a programmer and learned how to use the things, I never liked  Complier Directives.  Ever since I first encountered them, I thought they were ugly and there was nothing Object Oriented about them.  Before I even knew what Declarative Programming was, I was yearning for it.  I'll be the first to admit that Compiler Directives were a necessary evil in the past, emphasis on the evil part.  

Anyway, in comes .NET and there are these really cool things called Attributes that fix so much of what was broken in the programming world.  Creating Custom Attributes is a discussion unto itself (one I probably need to write about soon) but let me run through why I like them so much.

In the old days of ugly compiler directives you would mark code with Conditional statements and then define build constants.  When the code was compiled, the preprocessor directives indicated what would be executed and what wouldn't be.  Syntactically, they were clunky often looking like this:

#If Not Something Then

#End If

The problem is that code would often be littered with these things, and they quickly become a crutch.  Then little by little many projects suffered from Conditional Creep, where many Conditionals were added (I've personally worked on a project where there were over 10 of them in just one .dll.  You'd have to remember what they all were, remember when to set them and then verify that they were all set correctly.  Forgetting one of them could cause major problems.  Now, this was a lot better than keeping seperate code bases for different clients that had essentially the same program, or test/release builds, but it was still ugly.  Then in the code, they'd be all over the place.  Trying to debug this stuff was a nightmare because you first had to know what they all were, then what they meant, then where they were located.  You could use Find/Replace and other mechanisms, but there was no way around the fact that it was an ugly solution.  After all, aren't we supposed to be writing Object Oriented Code?  What is Object Oriented about PreProcessor directives?  Shouldn't I be able to set my Object's state and know what state my object is in when the program first runs, and respond accordingly?  Moreover, what if I want one object, or method/property of an object to resopnd to multiple possible conditions?  With many objects we define flags so we can set multiple properties of our object, and we've had this ability for years.   Why should it be different with compilation?  Lets say that I wanted my object to respond to the fact that the build is Release or Debug, that it's version is X or Y, that's it's being used InHouse or Distribution, and being deployed in NorthAmerica, SouthAmerica, Canada or Asia.  We'd set all of those preprocessor directives and away we'd go right?  Wrong.  Imagine what it would look like to have all of those Conditionals defined. And what takes precedence over what?  For instance, What if we went with the first of each of the choices on weekdays, but went with the second of the choices on Weekend builds.  (Obviously this part isn't realistic, but I want to make a point).  What would the branching look like?  What would come first in the IF Block?  And would you really want to write or read the code with all of this nested If logic?  The obvious answer is a resounding NO!

So how does .NET change any of this?  Let me walk through a really simple example.  Let's say that we have a project and if it's a Debug Build we want to use call the InitializeConnection method of our ConnectionManager class which points to our test database, otherwise we want to point to our production database. This is a pretty typical use of preprocessor directives so I think it will illustrate the point well.  Using Attributes, our code might look something like this:

public class ConnectionManager
{
  
private string DebugConnectString = "blah blah blah; testdatabase; blah blah ";
  
private string ReleaseConnectString = "blah blah blah; Releasedatabase; blah blah ";
  
private SqlConnection cn;

    [Conditional("DEBUG")]
  
private void SetConnectionDebug()
   {
       cn.ConnectionString = DebugConnectString;
   }

  
private void SetConnectionRelease()
   {
       cn.ConnectionString = ReleaseConnectString;
       }

  
public ConnectionManager()
   {
  

     //Create a new Instance of cn, but without
     //an initialized ConnectionString
      cn = new SqlConnection();
      
//If built in release this is the only one that will be set
     //(The ConnectString pointing to Production even though
     //we are calling SetConnectionDebug right afterware -
     //it's because SetConnectionDebug will never execute
      SetConnectionRelease();
      
//This one will reset the connection string to the Debug
     //Database if that attribute is defined
      SetConnectionDebug();        
        
   }
}

Now, by adopting a naming scheme, it's very clear what we are doing, and only one of these two scenarios can be true.  All we have to do is flip the build directive and if it's Release, SetConnectionDebug will never execute.  If it's Debug SetConnectionRelease will execute, but it will then be replaced with the Debug one.  I could have coded a more elegant example, but my goal is to show you how the things work.

At this point, I can hear all of the ludites out there saying "Big deal, preprocessor directives are more 'readable' from my point of view (why is it that readability is always used to defend bad programming habits?) and that didn't save me any code.  Well, at this point, there might be some merit to that, but that's not because using Attributes isn't vastly superior, it's because this discussion needs some more elaboration (And just for the record, you can still use Preprocessor directives and ATTRIBUTES, and that alone indicates their superiority if nothing else).  Let me elaborate on this point some.  There are many instances where we just use a block of code to do something.  We don't do it behind an object or a method or property.  We just write out a line of code (I guess in .NET it's still behind an object buy you get my point).  You can still code with your preprocessor directive here like so:

#if DEBUG
       cn.ConnectionString = DebugConnectString;
#else
      cn.ConnectionString = ReleaseConnectString
#endif

Notice something here!  The code in the #else is grayed out.  Why?  Because DEBUG and RELEASE are attributes and as soon as you use them (or anything else) the IDE looks to see if they are defined.  If they are, the IDE will have full Intellisense and make it obvsious that this is the one your are using.  If it isn't defined, then it will appear grayed out.  In this instance, my Build mode is set to Debug so the #else section is 'disabled'  That alone works wonders in helping you know what you defined without having to flip into the Project Properties window and see what you defined.  Since DEBUG and RELEASE are built into the IDE, it recognizes them without any intervention on your part.  Compare that to your own Compiler constants and the benefits of Attributes show themselves rather quickly.

But back to the other point.  One might argue that preprocessor directives are still good, but just make an adjustment and use the built in ones where applicable.  That still doesn't hold water.  You see, DEBUG and RELEASE are Attributes and you can build your own.  Then, you can set them in a configuration file and once again, at design time you know if something is set or not, have Intellisense and have a central place to manager everything.

Because .NET supports attributes, and you can attach them to any object, any method, any property, you all of a sudden have a lot of power.  Let's modify the scenario.  Using the connection manager object that we just had, lets say that we want to use the IDE to build our SQL Connections.  One reason many people don't use the designer is because they believe they will 'hard code' the connection string.  However, this isn't true.  You can use Attributes again, and Associate this attribute with your connections.  Then from one central place you can change all of them.  Also, if you use attributes, you can add as many as you want, they aren't mutually exclusive.  So, I could define a ConnectionString Attribute in my .config file and point all of my connections to it.  I could have a DEBUG and a RELEASE VERSION set up.  Then, I could change my ConnectionString whenever I wanted to without recompiling my project and I could switch from RELEASE to DEBUG on the fly (after all, in my example above I'm hard coding my ConnectionStrings which is flawed for many reasons from Security to Portability, but that's another story).  

So, drag a SqlConnection onto a form. From there, click on the Dynamic Properties in the Property Grid (they aren't called Dynamic for Nothing!) and click in the Advanced Property (since there can be muliple attributes set up, you'll notice the elipsis "..." in the right hand side.  When you click this, the Dynamic Properties Dialog will appear and on the left hand side you'll see a Properties ListBox with a ConnectString property, on the right hand side you'll see Key Mappings and in the combobox, SqlConnection1 which is the name of the current SqlConnection.  If you Check the ConnectionString Property the IDE will add a Configuration File (see below) named app.config for desktop apps or web.config for web apps (although ASP.NET creates config files by default)



if one doesn't already exist and add a Key for SqlConnection1.ConnectionString which will map to that property.  You should then enter the ConnectionString in the .Config file and from then on, the default ConnectionString for SqlConnection1 will be the string you entered.  Now, this is an Attribute of SqlConnection1. Your screen should look something like this:



Now, just to get comfortable with it, go to a totally different form and drag a SqlConnection onto it.  Then Select Dynamic Properties and expand the Advanced property again.  You'll see essentially the same thing, but if you click on ConnectionString you will have the option of reusing the Attribute for SqlConnection1 or creating a new one.  By Adding the same attribute to all of your connections, you can manage your ConnectionStrings very easily which still using the designer. You can also still use the Debug/Release model because of the support for multiple attributes.  When you are dealing with objects like Controls for instance, by default they'll have many more attributes (see below) and you can give all of the the same look and behavior just by taking advantage of attributes:



Compare this ability with good old Compiler Directives and you'd have to admit, You've come a long way Baby!

Just to be thorough, let me show you how easy it is to add multiple attributes to your class.  Let's say I used the button example above and I clicked on each of the properties in the dialog box to add them to the solution, here's basically what it would look like:

<?xml version="1.0" encoding="utf-8"?>
<configuration
>
  <appSettings>
     <!--   User application and configured property settings Vo here.-->
     <!--   Example: <add key="settingName" value="settingValue"/> -->
     <add key="sqlConnection1.ConnectionString" value="" />
     <add key="button1.AccessibleDescription" value="(None)" />
     <add key="button1.AccessibleName" value="(None)" />
     <add key="button1.AllowDrop" value="False" />
     <add key="button1.CausesValidation" value="True" />
     <add key="button1.Enabled" value="True" />
     <add key="button1.ImageIndex" value="-1" />
     <add key="button1.TabIndex" value="0" />
     <add key="button1.TabStop" value="True" />
  appSettings>
configuration>

And the Designer would add the following code to what it creates in the IDE:

//I left some of them out for brevity
this.button1.AccessibleName = ((string)(configurationAppSettings.GetValue("button1.AccessibleName", typeof(string))));
this.button1.AllowDrop = ((bool)(configurationAppSettings.GetValue("button1.AllowDrop", typeof(bool))));
this.button1.CausesValidation = ((bool)(configurationAppSettings.GetValue("button1.CausesValidation", typeof(bool))));
this.button1.Enabled = ((bool)(configurationAppSettings.GetValue("button1.Enabled", typeof(bool))));
this.button1.ImageIndex = ((int)(configurationAppSettings.GetValue("button1.ImageIndex", typeof(int))));
this.button1.Location = new System.Drawing.Point(104, 48);
this.button1.Name = "button1";
this.button1.TabIndex = ((int)(configurationAppSettings.GetValue("button1.TabIndex", typeof(int))));
this.button1.TabStop = ((bool)(configurationAppSettings.GetValue("button1.TabStop", typeof(bool))));
this.button1.Text = "button1";
this.button1.Click += new System.EventHandler(this.button1_Click);


Ok, that's how you'd add multiple properties in a declarative manner but these aren't attributes as such are they?  Not in the literal sense, but they do allow you to program in a declarative manner and that's why I bring them up.  Let's get back to literal attributes for a second.  I said you can define multiple attributes right?  So here we go:

Let's say we had two things we wanted to define when we build, DEBUG or RELEASE and InHouse or ShrinkWrap.  We could do the nested if thing or we could use multiple attributes, using our previous example and building on it:

[Conditional("DEBUG"), Conditional("DeployLocation")]
private void SetConnectionDebug()
{
    cn.ConnectionString = DebugConnectString;
}

In this instance, both must be true for this to execute which once again is much cleaner than the nested #IF's.  If you don't think that's very readable, you can use this syntax as well:

[Conditional("DeployLocation")]
[Conditional("DEBUG")]
private void SetConnectionDebug()
{
    cn.ConnectionString = DebugConnectString;
}

They will both compile and behave the same.  Now, let's whip up our own attribute.  One of the cooler examples I saw is a BugFix attribute.  Whenever a programmer fixes a bug, it'd be nice to have a record of it.  Sure tools like Source Safe can tell you that things changed, but you have to dig through them and compare them manually.  It's not often the fastest thing in the world.  So let's say your department implemented a MustUseAttributes rule and each time you made a bug fix, you noted it with an attribute.  Here's a class I got from Jesse Liberty's superb book
Programming C#:  

namespace MyCompany
{
  
/// <summary>
  /// This is an example from Jesse Liberty used in
  /// his Programming C# Book - http://www.oreilly.com/catalog/progcsharp/chapter/ch18.html
  /// I thought it was a particularly useful example and if you haven't
  /// purchased his book yet, I highly recommend it.
  ///
   [AttributeUsage(AttributeTargets.Class |
        AttributeTargets.Constructor |
        AttributeTargets.Field |
        AttributeTargets.Method |
        AttributeTargets.Property,
        AllowMultiple =
true)]
public class BugFixAttribute: System.Attribute
{
  
public BugFixAttribute(int bugID, string programmer, string date)
        {
          this.bugID = bugID;
          this.programmer = programmer;
           this.date = date;
         }
  }

public string Comment
{  
get{return comment;}
    
set{comment = value;}
}

public int BugID
   {
get{return bugID;}
}

Now, to use it in your code

[Conditional("DeployLocation")]
[Conditional("DEBUG")]
[BugFixAttribute(121,"Bill Ryan Thanks to Jesse Liberty","12/10/03")]
[BugFixAttribute(107,"Bill Ryan Thansk to Jesse Liberty","12/11/03",
    Comment="Moved the Database and changed the ConnectionString")]
private void SetConnectionDebug()
{
    cn.ConnectionString = DebugConnectString;
}

Quite a bit here, but yes, this will compile and it will work!  Now, you may be thinking that you can do all of this with the correct use of properties and .ini files.  To some end, you are correct.  But the fact that there are multiple ways to accomplish something doesn't negate its value.  Moreoever you can inherit from your attributes to name one thing, have company defined abstract attributes that fit into your configuration management system for another.  You can use Attributes to support all of this, even having other ones pointing to a database where this information is fed.  You can also dynamically declare these guys and accomplish more than you realize with them.

Anwyay, I wanted to make a point that things have changed in .NET and in some areas they have really changed.  Declarative Programming is a methodology who's time has come and while it may seem a bit abstract  and overly complex at first, I assure you that once you start using it, you'll fall in love with it quickly.  It's cleaner, easier to use, more flexible and really provides an object oriented way to deal with Object State.  And if you're really stubborn, at least you won't have to worry about compiler directives the same way you used to!

Writing Add-Ins for Visual Studio .NET
Writing Add-ins for Visual Studio .NET
by Les Smith
Apress Publishing