Click here to Skip to main content
Click here to Skip to main content

About #region preprocessor directive

By , 29 Aug 2010
 

I’m not going to introduce you to #region directive, it’s probably what every user of C# knows about and uses. What I want to discuss is their usage in your code. #region allows us to group some related code and then collapse it. Pretty. Now you don’t have to scan through a lot of code in a method that does something uninteresting, like:

public void DoSomething()
{
  bool shouldIDoSomething;
 
  #region Decide if I should do something
 
  if(needToDoSomething && haventDoneSomethingThisDay)
    shouldIDoSomething = true;
  else
  {
    // do some other logic to decide and set shouldIDoSomething to some value
  }
 
  #endregion
 
  if(shouldIDoSomething)
  {
    done++;
  }
}

That is over-simplified of course, in a real world, you can find a hundred lines and more in such region. Now, if we collapse it, it looks just neat, doesn’t it?

public void DoSomething()
{
  bool shouldIDoSomething;
 
  [Decide if I should do something]
 
  if(shouldIDoSomething)
  {
    done++;
  }
}

Okay, wait a minute now. You have just grouped some statements and named it properly. If you look closer, you have just created a new method, but it’s inlined into the current one! A function should do just one thing. This is one of the principles that are found in Clean Code book. Why don’t we extract the function into its own, so that both of them would do only one thing? Let’s see:

public void DoSomething()
{
  if(ShouldIDoSomething())
  {
    done++;
  }
}
 
private bool ShouldIDoSomething()
{
  if(needToDoSomething && haventDoneSomethingThisDay)
    shouldIDoSomething = true;
  else
  {
    // do some other logic to decide and set shouldIDoSomething to some value
  }
}

That is much cleaner, because you have reduced the complexity of the previous version of DoSomething method. Both can now be tested separately to ensure that none of the logic breaks.

So, what we learned here is: #region is not meant to be used in huge methods. Every time you use #region in a method, stop and think about what you’ve just created. Most of the time, you can extract that piece of code into its own method.

Next, we often see beautiful regions like these:

#region Get Customer
 
public void GetCustomer()
{
  // code to get the customer
}
 
#endregion
 
#region Save Customer
 
public void SaveCustomer()
{
  // code to save the customer
}
 
#endregion

When we collapse them, we get:

[Get Customer]
 
[Save Customer]

How does that make reading easier? What’s the point of it, I don’t get it? How is it better than just having the methods collapsed? It makes it even harder to read, because every time you see such region, you have to expand it and then expand the method. Silly, isn’t it? Don’t use #region just because you can!

And the next example is when we have regions like this:

public class PriceCalculator
{
  public decimal CalculatePrice()
  {
    decimal price = 100m;
    decimal discount = CalculateDiscount();
    return price * (1m - discount));
  }
 
  #region Discount Calculation
 
  private void CalculateDiscount()
  {
    decimal discount = 0m;
 
    if(CanApplyDiscount())
      discount = 0.05m;
 
    return discount;
  }
 
  private void CanApplyDiscount()
  {
    // some logic, other method calls
  }
 
  // some other discount calculation methods
  ...
 
  #endregion
}

Now if you compare this example with the first example I gave in this blog post, you might see the similarity. It’s exactly the same, but at class, not method, level. We have a similar principle here. According to the Single Responsibility Principle (SRP), a class should have only one responsibility. Look at the class above. You can easily spot two responsibilities here – price calculation and discount calculation. The discount calculation methods were already grouped, so why didn’t they get extracted into a new class? Like this:

public class PriceCalculator
{
  public decimal CalculatePrice()
  {
    decimal price = 100m;
    decimal discount = new DiscountCalculator().CalculateDiscount();
    return price * (1m - discount));
  }
}
 
public class DiscountCalculator
{
  public void CalculateDiscount()
  {
    decimal discount = 0m;
 
    if(CanApplyDiscount())
      discount = 0.05m;
 
    return discount;
  }
 
  private void CanApplyDiscount()
  {
    // some logic, other method calls
  }
 
  // some other discount calculation methods
  ...
}

Note: In real application, you might want to create and interface IDiscountCalculator and use it in PriceCalculator class. The goal is to decouple PriceCalculator from concrete implementations of IDiscountCalculator because you might want to change them without changing the PriceCalculator class.

So the moral here is: Always extract a group of related methods into a new class that has one concrete responsibility!

So, how to use the #region directive then? Well, it’s still useful for grouping things together. I usually have the more or less traditional regions in every class, to group the different constructs that we have in classes – fields, properties, methods, events, inner types. So usually my classes look like this when you open its file:

public class SomeClass
{
  [Events]
 
  [Fields]
 
  [Properties]
 
  [Methods]
}

So, to sum it all up, I see regions as a way to control the complexity of reading our source code. You can hide a group of related stuff in a region. However, it’s not an excuse to create monster methods or classes. Regions do not eliminate complexity – they only make it hidden when you read the code. So you must control the complexity of your source code by writing small, clear and focused methods and classes. When you accomplish that, you’ll notice that regions aren’t even necessary.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

gedgei
Software Developer
Lithuania Lithuania
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5membernikhi _singh1-Mar-12 0:25 
Nice one
General#region and try/catchmemberBosse6217-Sep-10 8:20 
Hi gecka!
I use my own standard template for all my functions. It includes a try/catch/finally block and I always return some kind of Error/NoError code in my functions. (When I want to have return values from functions I always use the 'ref' or 'out' keywords.) To get my code more readable I have used the #region to 'hide' the catch/finally blocks since they are almost always the same. Do you think that's a good or bad approach? Can it be improved? The 'catch' block can never be moved out from the functions so I say No to these questions but maybe you or someone else has a better solution? I'm open for suggestions.
GeneralRe: #region and try/catchmembergecka4-Oct-10 19:54 
Hello and sorry for the late reply.
 
The method you described here, returning error codes from every method, sounds so much like C. I imagine your code is more of error handling than logic. Personally, I'd never use such an approach. Methods should return values that they create/calculate, not error codes. Most of the time it will return success, so why would you sacrifice readability of your code for error handling? I'd replace the error codes with exceptions instead.
 
I also imagine your catch blocks might all look pretty similar. That would be a violation of DRY (Don't Repeat Yourself) principle. If you really want to keep the catch blocks and remove duplication, maybe Aspect Oriented Programming tools can help you. I haven't really used it for real, but AFAIK you can define a block of code that you can apply to selected methods in your code at runtime. That means your code stays in one single place.
 
That's just my opinion though, I haven't seen your code, so I might be also wrong about some things.
GeneralRe: #region and try/catchmemberBosse627-Oct-10 10:38 
Hi gecka
 
and thank you for giving me a reply. I totally agree what you are saying and I will change my approach when coding for .NET programs. You are so right when saying that my 'catch' blocks almost always are the same. Many repeating statements. But I forgot to tell you that I almost always are coding for COM which implies that I can not EVER send exceptions to the COM client. I just have to be repeating myself in the code... or? Maybe a lame exceuse but that's the reason why I code like I do. However, you're right, it is not the best approach when coding in the .NET environment only. I'll shape up!
Thanks for your input.
/Bosse62
GeneralMy vote of 5memberJason Down17-Sep-10 6:04 
Great points in their about SRP and and loose coupling.
GeneralMy vote of 5memberfreegemini12-Sep-10 9:16 
very usefull
GeneralMy vote of 5memberzhuqil6-Sep-10 22:52 
Great article,thank you very much!
GeneralAbsolutely rightmemberDrWheetos31-Aug-10 8:29 
I've been picking this thing up in code reviews where region blocks were placed inside a method. That shouldn't be the answer to breaking up a monolithic method into manageable chunks. That's what private functions are for.
 
Hi 5 from me.
Generalmy vote of 5 alsomembersuggie31-Aug-10 5:22 
You suprised me - what could I learn about #region preprocessor?
 
Now you've got me looking at my existing code and checking if I've mis-used it!
Happy to say no egregious examples, but thanks for the heads up.
GeneralMy vote of 5memberHerman Cordes30-Aug-10 21:35 
Great article, fully agree!

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130617.1 | Last Updated 29 Aug 2010
Article Copyright 2010 by gedgei
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid