The System.Diagnostics.Conditional attribute has been on blog-todo list for a while, and I'm finally getting around to it. For the quiz-loving folks, here you go:
Quiz:
First, some lingo: I'll say a Conditional Function is a function with a Conditional attribute on it. The conditional function is active (within the scope of a symbol) if the symbol is defined, else it is inactive.
If you aced that, you can skip the rest of this entry. About half of them could be categorized as trick questions...
Here's some background that helps answer those questions.
Conditional attributes can be placed on methods (and attributes in whidbey) to instruct the compiler to conditionally remove calls to the function if a symbol is not defined. This can be useful for debug-only functionality, like Debug.Assert, which has a Conditional("DEBUG") on it.
Conditional takes a string argument. If that string is defined (as determined by the compiler's preprocessor), then the compiler emits the method call. If the symbol is not defined, C# still compiles the method, but does not compile the calls.
A quick demo:
Consider the following file x.cs:
using System; using System.Diagnostics; class Program { [Conditional("Test")] static void Test() { Console.WriteLine("'Test' is defined"); } static void Main() { Console.WriteLine("Start"); Test(); Console.WriteLine("End"); } }
When compiled without defining 'Test', the method is not executed.
C:\temp>csc x.cs & x.exeMicrosoft (R) Visual C# 2005 Compiler version 8.00.50727.1378for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727Copyright (C) Microsoft Corporation 2001-2005. All rights reserved. StartEnd
C:\temp>csc x.cs & x.exeMicrosoft (R) Visual C# 2005 Compiler version 8.00.50727.1378for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.
StartEnd
But when defining 'Test', the method is executed.
C:\temp>csc x.cs /d:Test & x.exeMicrosoft (R) Visual C# 2005 Compiler version 8.00.50727.1378for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727Copyright (C) Microsoft Corporation 2001-2005. All rights reserved. Start'Test' is definedEnd
C:\temp>csc x.cs /d:Test & x.exeMicrosoft (R) Visual C# 2005 Compiler version 8.00.50727.1378for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.
Start'Test' is definedEnd
About Conditional Functions:
1. Compiler vs. CLR:
The Conditional attribute is entirely handled by the compiler without any cooperation from the runtime. The method is still jitted normally, but the compiler just doesn't emit the calls if the symbol is not defined. You can verify this by running ildasm on the output.
However, by including the Conditional attribute in the BCL, the CLR is essentially defining a cross-language protocol, and then leaving it up to the different languages to implement this protocol.
2. Compiler Enforcement:
Since it's a compiler thing, the compiler has to do most of the enforcement to provide the model it wants.
In C#'s case:
C#'s decisions here enforce an intuitive behavior: if the conditional functions are side-effect free, then removing them will not change program behavior or compiler warnings. (Exercise to reader: how would that break if C# didn't have the policy above).
But different compilers could have different policy decisions here. For example, a compiler could evaluate arbitrarily complex expressions in the condition. MC++ just ignores the attribute.
3. CLR things.
Since the method is still compiled in, it can be referenced and invoked by Reflection. For example:
MethodInfo m = typeof(Program).GetMethod("Test"); m.Invoke(null, new object[] { });
The Conditional attribute just affects the callsite and not the definition, so you can invoke conditional functions from across modules. This is critical for Debug.Assert(), which is defined in system.dll.
Also, other .NET languages could have their own policy about how to handle Conditional attributes. For example, a naive .Net language (such as ILasm) would not even check if a callsite has a conditional attribute, and emit the call regardless.
So what if you put a conditional attribute on Main()?
This is a strange case. A compiler could flag an error here since the user's intent is not clear. Does the user want the whole program to be a nop? In the absence of a compiler error, the behavior here is settled according to the existing rules (and loopholes in the compile). If the CLR makes the call to Main(), then the conditional attribute is ignored. This is what happens in C#. (See 'What runs before Main()' ) If you use 'void Main()', the the Conditional attribute is ignored in C#. If you use 'int Main()', then you get an error that the conditional must be on type void.
What about Conditional on Attributes?
Conditional attributes are basically the same philosophy of conditional functions, but applied to attributes.