Friday, February 05, 2010

ConditionalAttribute("DEBUG")

This is quite a neat concept that I only partially grasped until now. When you apply this attribute to a target (either a method or class) it is compiled just as any other custom attribute is treated. Look at the assembly in a tool like .NET Reflector and you'll see it there, exactly as you defined it. Bog standard.

The clever part is what happens next, whether you're referencing the target yourself (perhaps even from the same assembly in which it is defined), or someone else is referencing it by linking to your assembly as a 3rd party library.

The compiler recognizes the target has a conditional attribute, and it checks its list of defined symbols to see if there's a match. If there's a match, it proceeds as normal, but if there's no match, the compiler modifies the caller to leave the method call (or the custom attribute declaration) out of the newly compiled assembly entirely. Simple as that. In this way, the caller (and this is the important part) will never know that the target method or attribute class exists. This can have great performance implications: code that's compiled with the DEBUG symbol defined (or whatever symbol you choose for your project) will make all the calls, which could include very verbose tracing, but code without the symbol will have the method calls (and custom attributes) elided. In the case of method calls, not only the method call is skipped, but any logic for building up arguments is also skipped. For example:

ConditionalTargetClass conditionalTargetClass = new ConditionalTargetClass();
conditionalTargetClass.conditionalTargetMethod(Factorial(2000));


becomes

ConditionalTargetClass conditionalTargetClass = new ConditionalTargetClass();

There are a few limitations:
When applying the attribute to a method, that method return type must be void.
When applying it to a class, the class must derive from System.Attribute.

Another example:
[Conditional("DEBUG")]
public class DebugAttribute : Attribute { /* EMPTY */ }

[Debug]
public class Program { /* EMPTY */ }


becomes

[Conditional("DEBUG")]
public class DebugAttribute : Attribute { /* EMPTY */ }

public class Program { /* EMPTY */ }

No comments: