C# regions were introduced way back in 2003 via Visual Studio .NET as a way to name and collapse a block of code using the keywords #region to open and #endregion to close a region. The result looks something like:
This concept of collapsible regions came before the time of collapsible namespaces, classes, methods, if-statements, and for loops so it may have been a welcome addition for large portions of generated or boilerplate code.
In today's C# we don't usually have the same need to clear screen space and remove large chunks of code we don't want to see. Generated code is often created in separate files, our blocks of code collapse into neat single lines, and as good developers we're all using the "each method has one job and a descriptive name" rule -- right? 🤓
Using regions in the web service endpoint I'm building at work led me to the question "What are good region practices?" and consequently, to writing this article because there is a number of articles expressing hate for regions and only a few very short (and not very good) statements expressing why they can be used for good.
C# Regions Are Evil
One of the top hits on my search was an article written by Marc Duerst, simply titled: C# Regions are evil. His main points of the article make sense and are absolutely reasons to be wary of your use of regions. He describes regions as painful to the next developer who has to read your code - they get to expand dozens of collapsed sections to view the full picture. Some developers also get in the bad habit of hiding large code blocks in regions instead of refactoring them and considering how their structure could be improved.
Duerst also states that if it were possible for Microsoft to remove regions completely, it would be a good thing, but because it's not we should simply stop using them completely.
Regions Are a Code Smell
Another high hit on my Google search was Regions Are a Code Smell by Erik Dietrich. This article is written more from a middle ground and isn't really blaming the regions themselves, more of his focus is on the developers using bad practices: "regions do not make your 800 or 8000 line classes any less awful and smelly than they would be in a language without regions." He more specifically calls regions code deodorant or code cologne which works for code in much the same way deodorant or cologne works for smelly people - it hides the smells!
Dietrich believes there's no point in removing or rallying against regions because bad developers won't stop making bad choices simply because you've told them to stop using regions.
C# Regions Are To Be Used Gently
I agree with both of these authors for the most part and will stand up and shout from the rooftops that I have used regions for evil in the past. (The first step is admitting, right?) I was new, following the patterns in place by the developers before me, and didn't know any better yet.
Today is a different story though. Today I choose to use regions only in specific situations and would not rally to have them removed because they do occasionally provide value to me in my code and as Dietrich said, it wouldn't stop developers from making bad code smells even if we did.
I don't use regions in the majority of my projects and if I feel the need to make a region I consider the reasons why. Most often it's that I've built a chunk of similar code that is not similar to the code surrounding it. This means I need to create a new method or class to house this chunk of code and other parts that relate to it. Another case may be that my class is just getting too long - that's another great opportunity to consider how I can refactor my code and see what can be pulled out to create a new class. An example: "There's a bunch of Salesforce methods in this Sales Order creation class, why don't I pull those out and make a Salesforce Helper type class to house all of that functionality?"
The WCF Web Service Application I'm currently building onto at work is one of the rare situations where I have opted to use regions - and several of them at that. I will give you some details into this so you can form an idea why I've chosen to use regions:
- I was asked to follow the structure already in place in the application. Currently there are five listener endpoints that watch for various Salesforce Outbound Messages to fire. The application's classes are limited to the interfaces and services with a rigid naming standard. This means no breaking out sets of similar methods into new classes for readability and unfortunately makes for a very long Listener class.
- The listener is reading and updating many different portions of our Salesforce org as well as our ERP database, so there will be many large sets of similar variables all contained in one portion of the Listener class. This is normally frowned upon, but to be able to collapse fifteen Customer variables and ten Employee variables so I can view my RMA variables is a lifesaver (and helps with organization, so they don't end up mixed into one another).
- This project is on a stricter time requirement and my next task will be taking many of these regions out into completely new applications because many of these methods are used across our entire organization and currently require us to make code updates in 2-5 different places when one change needs to be made. Having them organized and separate will make that job easier.
So how do I keep these regions from deodorizing my code? The biggest step is avoiding the use of a region until I've considered the code that will go into it "done". This means it's been written, tested to ensure it works, and refactored out into small well-named methods. I pull these methods out, much as I'd pull them out to put them in a new class, and store them in their region at the bottom of the class (please, do not leave regions randomly scattered between your methods!) In this case, I am essentially considering my regions to be their own class-like entities and handle them as such. If you're curious, my variables are in regions like so:
Once again I'd like to reiterate that while I realize these are not best practices, sometimes as developers we are restricted to criteria (that may not be best practices themselves) that require us to bend some rules. I believe that as long as we knowingly bend these rules and use the best practices possible for our situation, we can sleep soundly knowing we wrote the best code we could. Even if it contains regions. If you're thinking about breaking code out into a region stop for a moment, ask yourself why, and consider whether it's due to a need to refactor or to hide something yucky you've done.