This is the archived version of Roland Weigelt's weblog that ran from 2003 to 2023 at weblogs.asp.net

Commenting out Code in C# (Oldie but Goldie Tip)

I usually “comment out” code

  • when something does not work as expected and I am looking for a workaround, or
  • when I want to quickly try out an idea for an alternative solution

and

  • I feel that using source control is too much hassle for what I’m trying to do in this situation.

How?

The term “comment out” obviously comes from using code comments to hide source code from the compiler. C# has two kinds of comments that can be used for this, each with their pros and cons:

  • End-of-line comments (“// this is a comment”) can be nested, i.e. you can comment a commented line. But they do not explicitly convey the start and the end of a commented section when looking at adjacent commented lines.
  • Block comments (“/* this is a comment, possibly spanning several lines */”) do have an explicit start and end, but cannot be nested; you cannot surround a block comment with a block comment.

For disabling full lines of code, a different approach is to use the “#if … #endif” directive which tells the C# compiler to consider the code inside only if the specified symbol is defined. So to disable code, you simply specify a symbol that does not exist:

#if SOME_UNDEFINED_SYMBOL
	...one or more lines...
#endif

Using the preprocessor for this purpose is nothing new, especially to those with a C/C++ background. On the other hand, considering how often I watch developers use comments in situations where “#if” would have clear advantages, maybe this blog post benefits at least some people out there.

Why #if?

“#if” directives can be nested

The C# compiler does not blindly look for a closing “#endif”; it understands which “#endif” belongs to which “#if”, so e.g. the following is possible:

#if DISABLED
	...some code...
#if DEBUG
	...debug code...
#endif
	...some code...
#endif

You can express why you disabled a specific block of code

For example, you could use “#if TODO” when you are working on new code, but want to quickly run the program again without that code before continuing.

Something like “#if TO_BE_DELETED” (or maybe “#if DEL” for less typing) could mark code that you intend to remove after compiling the project and running the unit tests. If you are consistent with the naming of the symbol, performing a cleanup across the project is easy, because searching for “#if SYMBOL“ works well.

Obviously, you could choose more descriptive symbols (e.g. “#if TODO_DATA_ACCESS” and “#if TODO_CACHING") to differentiate different places of ongoing work. But if you think you need this, it could be a sign you are trying to juggle too many balls at once.

“#else” is great while you work on replacing code

Sometimes, when I have to deal with a non-obvious or even buggy third-party API, writing code involves a lot of experimentation. Then I like to keep old code around as a reference for a moment:

#if OLD_WORKAROUND
	...old code...
#else
	...new code...
#endif

You can easily toggle code on/off

You can enable the disabled code simply by defining the symbol, either using “#define” in the same source file or as a conditional compilation symbol for the whole project.

Alternatively, you can invert the condition for a specific “#if” with a “!” in front of the symbol:

#if !DISABLED
	...some code...
#endif

Being able to quickly switch code off and back on, or – in conjunction with “#else” – to switch between old and new code without losing the exact start and end positions of the code blocks is a huge plus. I use this e.g. when working on interactions in GUIs where I have to decide whether the old or the new code makes the user interface “feel” better.

Notes on disabled code

  • Disabled code in a code base should always be viewed as a temporary measure, because code that never gets compiled “rots” over time. To the compiler/IDE, disabled code is plain text that is not affected by refactoring or renaming, i.e. at some point it is no longer valid.
  • Try to get rid of disabled code as soon as possible, preferably before committing to source control.
  • Last, but not least: Consider using source control for what it is good at - it exists for a reason. For instance, when experiments involve changes to many files, a new branch may be the better choice.

6 Comments

  • Thanks for the reminder Roland. I am just not in the habit, and will put in some effort to flip a switch in my brain to use this more.

    I noticed you can also just use #if true or #if false

    Regards,
    David

  • @David: Habit is definitely a factor, and the Visual Studio hotkey Ctrl+K, Ctrl+C to comment a section (using end-of-line comments) is quick and easy. I just blogged about what the (rough) equivalent would be for "#if ... #endif" here: https://weblogs.asp.net/rweigelt/quick-if-endif-in-visual-studio (tl;dr: Ctrl+K, Ctrl+S, down arrow, Enter, SYMBOL, Enter).

  • @WeigeltRo - Ah. I like the keyboard shortcut for the #if ... #endif. I was about to comment on that. Ctrl-K, Ctrl-C is just so easy. Hard to get out of that habit. But if I can use #if with just a couple more keystrokes, then I might be more inclined to use it. I still think I use Ctrl-K, Ctrl-C for something that I know is going to be fleeting. Because the other issue with #if SYMBOL is now I have to change my build also, unless I want to just cheat and quickly add a #define.

  • @Chris: I'm not quite sure about what you mean by "...issue with #if SYMBOL is now I have to change my build also". If you *don't* change your build, SYMBOL will be undefined, which will have exactly the desired effect, i.e. the code will be disabled. You don't have to set SYMBOL explicitly to false.

  • You mention source control as being "too much hassle for what I’m trying to do".

    This is generally the answer given to people who hated commented-out code, and insist you use source control instead.

    However, usually, the correct response is: "Source control is not an option here".

    Consider: we are commenting out block A so we can use block B instead. We are doing this because block A is not working correctly (or at least not optimally). We haven't even entered block B yet. Hence using source control, in this case, would require checking in the broken Block A --- which is something we never want to do.

  • @James: By your use of the term "checking in" I guess that you are using a centralized source control system. At work, I have been using TFVC as part of a development team for over a decade. In that context, checking in code is serious business because it affects other people.

    For my private projects, I started using git three years ago. With git as a distributed version control system, I'm free to experiment in branches that never leave my machine. In git, branches are quick and easy to create - and to throw away after I brought the essential changes into the normal development branch. Having different branches for different experimental approaches is not expensive. And what ends up in the resulting source control history of the "develop" and "master" branches (which I push to a repository on GitHub) is completely my decision, because git gives me the freedom to clean up the history.

    So for experimental work, there *is* indeed an overlap between using branches in a (distributed) source control system and "#if ... #else ... #endif". Obviously, for changes to a single file or only a few files, "#if" is the pragmatic choice. At the same time, I keep an open eye on when experimental changes start growing across the whole project.