Wednesday, March 23, 2011

Will conditionals that are always false be removed by compiler in C#

Say I have the following code snippet in c#

static const bool DO_PERFORMANCE_CODE = false;

if (DO_PERFORMANCE_CODE)
{
   // performance monitoring code goes here
}

Will that code get removed by the compiler? This is the functionality I'd like. Basically I want to mimic the conditional compilation stuff in C#, but I want more configurations other than Release and Debug. If there is a better way of doing this, I'd be open to hearing it.

From stackoverflow
  • When you build in "debug" the preprocessor variable DEBUG is defined. So, you can do this:

    public void MyFunc()
    {
    //do release stuff
    #if DEBUG
    //do performance testing
    #endif
    //finish release stuff
    }
    

    and it will be ignored by the compiler when you switch to Release mode.

    Alternatively, you can define your own preprocessor variable if you don't want to test in debug mode, making sure to "#define PERFORMANCE_TESTING" when you want to test, and commenting it out when you don't.

    #define PERFORMANCE_TESTING
    
        public void MyFunc()
        {
        //do release stuff
        #if PERFORMANCE_TESTING
        //do performance testing
        #endif
        //finish release stuff
        }
    
    Aaron Smith : I thought when I saw your answer you only had the #if DEBUG part in there. I'll give you an upvote since he said he wanted more than DEBUG and release.
    scottm : Honestly, I did. Then I realized if you are focused on performance, you might want to do testing in release mode.
    Jonathan Beerhalter : Is it possible to have nested #ifs? I would assume so, but I'm not sure.
    scottm : Yes, you can nest multiple preprocessor directives.
    danio : And it can get very hard to follow very quickly
    sixlettervariables : The [Conditional] attribute is also nice when your performance code is in a method.
  • You can define your own precompiler variables.

     #define temp
    
     #if temp
     // Do something
     #endif
    
  • you can define your own constants either at the top of the file:

     #define SUPERDOOPERDEBUG
    
     using System;
    
     ....
    
    
     #if SUPERDOOPERDEBUG
    
         // something
    
     #endif
    

    Also, you can set up different build configurations than just 'release' and 'debug' (See the Build -> Configuration menu or define additional Conditional compilation symbols in the Project -> properties menu

    Aaron Smith : Wow, I was 14 seconds ahead of you.
    Jimmy : I'll get you next time, Inspector Gadget!
    Aaron Smith : And you get upvoted and I don't. :-(
  • You can define more symbols other than DEBUG/RELEASE ... just look in the project properties :-) then you can use the preprocessor directive syntax to optionally include things based on the project configuration

  • You aren't limited to DEBUG and RELEASE. You can specify other constants with #define or in the project properties.

    lc : Ha. #5 with the same answer. Sorry to clutter.
  • Defining your own symbols is probably cleaner, but I was curious as to what the compiler would do, so I did some experimenting (using VS 2008 in release mode).

    With this:

    class Program
    {
        static void Main(string[] args)
        {
            bool foo = false;
    
            if (foo)
            {
                Console.WriteLine("Hello, world?");
            }
        }
    }
    

    The compiler still generates the code for the if statement:

    .method private hidebysig static void  Main(string[] args) cil managed
    {
      .entrypoint
      // Code size       16 (0x10)
      .maxstack  1
      .locals init ([0] bool foo)
      IL_0000:  ldc.i4.0
      IL_0001:  stloc.0
      IL_0002:  ldloc.0
      IL_0003:  brfalse.s  IL_000f
      IL_0005:  ldstr      "Hello, world\?"
      IL_000a:  call       void [mscorlib]System.Console::WriteLine(string)
      IL_000f:  ret
    } // end of method Program::Main
    

    If instead you do:

    class Program
    {
        static void Main(string[] args)
        {
            bool foo = false;
    
            if (false)
            {
                Console.WriteLine("Hello, world?");
            }
        }
    }
    

    It doesn't generate code for the if statement:

    .method private hidebysig static void  Main(string[] args) cil managed
    {
      .entrypoint
      // Code size       1 (0x1)
      .maxstack  8
      IL_0000:  ret
    } // end of method Program::Main
    

    It also seems to skip the variable that is assigned a value that is never used.

    I checked with ildasm.exe, the disassembler that comes with visual studio.

  • I ran a bunch of tests with Reflector and Visual Studio 2008 SP1, and this is what I found.

    1. Inside of a false conditional compilation (#if SOMETHING_FALSE...), the code is not only not compiled, but the compiler doesn't even complain about errors
    2. In a code block inside of an always false conditional based on a constant (ie. if(false) { ... or if(constant_declared_false) { ...) variable declarations of both value and reference types are compiled, but no executable code is compiled (method calls, for loops, setting the value of a variable, etc).
    3. If I set a local variable to false and test for the local variable, all code is compiled.

0 comments:

Post a Comment