What does code using a ConditionalAttribute look like under the debugger?

What does code using a ConditionalAttribute look like under the debugger?

  • Comments 2

Last week, Nathan posted about the ConditionalAttribute.  This got me thinking...  What does his example look like under the debugger?  To find out, I built a very simple application - a NetCF console application containing Nathan's two methods (foo and bar) along with a Main that calls foo.  I'll use NetCF v1 SP2 and cordbg version 1.1.4322.573 (from the .NET Framework SDK v1.1).

CONDITION1 defined
The first example defines CONDITION1.  In this example, we expect to be able to call both foo and bar.  Source view shows the implementation of both methods.

Source view

(cordbg) sh 25
001: #define CONDITION1
002:
003: using System;
004: using System.Diagnostics;
005:
006: namespace quicktest
007: {
008:    class Class1
009:    {
010:            static void Main(string[] args)
011:            {
012:*                   foo();
013:            }
014:
015:            /** foo
016:
017:            */
018:            static void foo()
019:            {
020:                    Console.WriteLine("Entered foo");
021:
022:                    bar();
023:                    // <some code>
024:
025:                    bar();
026:                    // <some code>
027:
028:                    bar();
029:                    // <some code>
030:            }
031:
032:            [Conditional("CONDITION1")]
033:            public static void bar()
034:            {
035:                    Console.WriteLine("Entered bar");
036:            }

Now, let's disassemble foo to see what the IL looks like.

IL view (foo method)
To get to the IL, we need to step into foo.  As mentioned in my debugger series, when debugging NetCF applications, cordbg dis[assemble] command displays IL.

020:                    Console.WriteLine("Entered foo");
(cordbg) dis 5
*[IL:0000] 72:01000070      ldstr       70000001
 [IL:0005] 28:0200000a      call        System.Console::WriteLine
 [IL:000a] 28:03000006      call        quicktest.Class1::bar
 [IL:000f] 28:03000006      call        quicktest.Class1::bar
 [IL:0014] 28:03000006      call        quicktest.Class1::bar
 [IL:0019] 2a:              ret

We can clearly see that the three calls to bar are present in the IL.  Stepping into each confirms this.

IL view (bar method)
Just for fun, here's the disassembly of bar.

035:                    Console.WriteLine("Entered bar");
(cordbg) dis
*[IL:0000] 72:19000070      ldstr       70000019
 [IL:0005] 28:0200000a      call        System.Console::WriteLine
 [IL:000a] 2a:              ret

At this point, everything looks normal, as if the ConditionalAttribute wasn't in use.

CONDITION1 undefined
Now, let's see what happens if we do not define CONDITION1.

Source view
In source view, everything looks the same (minus the #define at the top).

(cordbg) sh 25
001: using System;
002: using System.Diagnostics;
003:
004: namespace quicktest
005: {
006:    class Class1
007:    {
008:            static void Main(string[] args)
009:            {
010:*                   foo();
011:            }
012:
013:            /** foo
014:
015:            */
016:            static void foo()
017:            {
018:                    Console.WriteLine("Entered foo");
019:
020:                    bar();
021:                    // <some code>
022:
023:                    bar();
024:                    // <some code>
025:
026:                    bar();
027:                    // <some code>
028:            }
029:
030:            [Conditional("CONDITION1")]
031:            public static void bar()
032:            {
033:                    Console.WriteLine("Entered bar");
034:            }

Now, lets go back to foo and see what the IL looks like now...

IL view (foo method)

018:                    Console.WriteLine("Entered foo");
(cordbg) dis
*[IL:0000] 72:01000070      ldstr       70000001
 [IL:0005] 28:0200000a      call        System.Console::WriteLine
 [IL:000a] 2a:              ret

When you look at the IL, for foo, the three calls to bar are not there.  They are "commented out" by CONDITION1 not being defined!

So, has bar met with the same fate?  Or does it still get compiled into the app?

IL view (bar method)
Since foo no longer calls bar, we'll try to break into it using a funceval.

(cordbg) b 33
Breakpoint #1 has bound to <path>\quicktest.exe.
#1      <path>\class1.cs:33        bar+0x0(il) [active]
(cordbg) f quicktest.Class1::bar
break at #1    <path>\class1.cs:33        bar+0x0(il) [active]

033:                    Console.WriteLine("Entered bar");
(cordbg) dis
*[IL:0000] 72:19000070      ldstr       70000019
 [IL:0005] 28:0200000a      call        System.Console::WriteLine
 [IL:000a] 2a:              ret

As you can see, the method decorated with the ConditionalAttribute is still part of your application - the code must compile if you use this attribute.  Pretty neat!

Well, that wraps things up for today. 

Enjoy!
-- DK

[Edit: Fix typeo]

Disclaimer(s):
This posting is provided "AS IS" with no warranties, and confers no rights.