views:

204

answers:

6

How do you recommend using #region / #endregion? To what extent should that replace using sub functions to clarify your code?

+20  A: 

Not at all.

First of all, #regions are more a way of grouping many related functions/members into collapsible regions. They are not intended to structure a single multi-thousand line function into parts. (That being said, if you write a single method that's so long that you consider structuring it with #regions then you're probably doing something seriously wrong. Regions or not, that code would be unmaintainable. Period.)

Many people argue however, that it doesn't really help and that you should consider rewriting classes that actually need regions to be understandable. Also, regions tend to hide nasty code.

Joey
Totally agreed. Personally I hate regions because they hide the code. I've also configured custom templates for interface implementations so that Visual Studio doesn't automatically insert `#region ISomeInterface Members`.
Darin Dimitrov
Agree totally. Each method should fit on a printed page where possible and hiding the length in #regions is just bad mojo.
Godeke
I will ditto the 100% agreement. If it needs regions it also needs refactoring
dkackman
Agreed that regions are not an alternative to proper structure, and used incorrectly, can serve to hide problems. Having said that, they're still useful for organizing properly structured code for easy reading.
Steven Sudit
only use regions when you can live without them (seriously) and it is just for eye candy. Personally I hate collapsing code as it hides what I am interested in: The code.
Johannes Rudolph
I find that I'm rarely interested in *all* the code at the same time, so collapsing some of it lets me find what I actually care about.
Steven Sudit
While I agree with this answer in principle, I think the use of regions that I describe in my answer is totally defensible. It's not common, but some code is really genuinely OK to hide.
Robert Rossney
One of the more easily defended uses is to hide away generated code.
Steven Sudit
With the introduction of partial classes the need to hide generated code practically went away.
flq
+3  A: 

#region / #endregion is a way to logically group parts of the code belonging to the same class. Personally, I tend to group private field declarations, properties, public functions and private functions.

Sometimes I use those keywords to group some parts of the code which I need to look after and update often, for instance, calculation methods.

Anax
A: 

If you have more than one 'logical group of code' in a class, your class violates the single responsibility principle.

Sort that out and you no longer need regions.

Alun Harford
So you wouldn't consider properties, constructors and methods to be logically groupable?
Dan Diplo
I think he means groupable by functionality. Also, if you have enough constructors, methods, or properties that you feel the need to collapse it into regions, you class may be too large. Not necessarily, but quite often this is the case. I've started breaking classes up more lately than I used to, and the code becomes surprisingly more readable and usable, even though it always worked fine before.
Snarfblam
@Dan - Sure they are. They're members of the same class. If you have so many of them that it makes sense to wrap them in regions, you need to rethink your design.
Alun Harford
A: 

Regions seem good in theory, but in my experience, it's a feature that is often abused.

Programmers love order; most folk love tidying things away into little boxes. They group messy code, fields, properties, constructors, methods, public methods, internal methods, private methods, helper methods, constants, interface implementations and God knows what else.

The only thing I can think of that irks me more is the use of partial classes to hide complexity.

Anyway, while over-use of regions is often a tell-tale sign of hiding a mess that shouldn't be there, I've also seen good code swamped by them. I've downloaded a few open source projects written by respected programmers. These fellows are writing some amazing code, but, oh, what's this?

One field? A field region! Two properties? A property region! One constructor? A constructor region! One private method? A private method region!

I could go on.

To this very day, I am still astounded when I see this. In some cases, a region, a blank line, another blank line and the end region can take up 5x the space of the original code (5 lines with regions, 1 line without). It's basically a form of OCD; these regions may appeal to our sense of order during the act of writing software, but in practice they're useless -- pure noise. When I first started writing c# I abused them in this way, too. But then I realised how noisy my code was, and that hitting ctrl-k l every time I open a file is a sign that I was doing it wrong.

I can understand it when a class implements an interface that has a lot of properties (e.g. for databinding) or even a group of methods for implementing some related functionality, but for everything?. It makes no sense.

I still use regions now and then, but... I exercise a lot of restraint.

Mark Simpson
A: 

The only circumstance I've ever found where I felt using a region was totally okay is in the code below. Once I got it right, I never wanted to have to look at those constants again. Indeed, I use this class every day, and I think the only time I've uncollapsed this region in the last four years was when I needed to reimplement it in Python.

I think (hope, pray) that the circumstances of this code are an edge case. C# constants based on a VB3 type declaration that defines how the COBOL data structure returned by a C++ function is laid out. Yeah, I ported this to Python. I'm that good. I'm tempted to learn Haskell just so that I can rewrite my Python code in it, with an eye towards one day reimplementing my Haskell code in OCaml.

    #region buffer_definition
    /*
  The buffer is a byte array that is passed to the underlying API.  The VB representation of
  the buffer's structure (using zero-based arrays, so each array has one more element than
  its dimension) is this:

  Public Type BUFFER_TYPE
   Method As String * 50
   Status As Integer
   Msg As String * 200
   DataLine As String * 1200

   Prop(49) As String * 100

   Fld(79) As String * 20
   Fmt(79) As String * 50
   Prompt(79) As String * 20
   ValIn(79) As String * 80
   ValOut(79) As String * 80
  End Type

  The constants defined here have the following prefixes:
   len = field length
   cnt = count of fields in an array
   ptr = starting position within the buffer
 */

    // data element lengths
    private const int len_method = 50;
    private const int len_status = 2;
    private const int len_msg = 200;
    private const int len_dataLine = 1200;

    // array elements require both count and length:
    private const int cnt_prop = 50;
    private const int len_prop = 100;

    private const int cnt_fld = 80;
    private const int len_fld = 20;
    private const int len_fmt = 50;
    private const int len_prompt = 20;
    private const int len_valIn = 80;
    private const int len_valOut = 80;

    // calculate the buffer length
    private const int len_buffer =
        len_method
        + len_status
        + len_msg
        + len_dataLine
        + (cnt_prop * len_prop)
        + (cnt_fld * (len_fld + len_fmt + len_prompt + len_valIn + len_valOut));

    // calculate the pointers to the start of each field.  These pointers are used
    // in the marshalling methods to marshal data into and out of the buffer.
    private const int PtrMethod = 0;
    private const int PtrStatus = PtrMethod + len_method;
    private const int PtrMsg = PtrStatus + len_status;
    private const int PtrDataLine = PtrMsg + len_msg;
    private const int PtrProp = PtrDataLine + len_dataLine;
    private const int PtrFld = PtrProp + (cnt_prop * len_prop);
    private const int PtrFmt = PtrFld + (cnt_fld * len_fld);
    private const int PtrPrompt = PtrFmt + (cnt_fld * len_fmt);
    private const int PtrValIn = PtrPrompt + (cnt_fld * len_prompt);
    private const int PtrValOut = PtrValIn + (cnt_fld * len_valIn);

    [MarshalAs(UnmanagedType.LPStr, SizeConst = len_buffer)]
    private static byte[] buffer = new byte[len_buffer];

    #endregion
Robert Rossney
A: 

I think that functions should only be used for reusable code. Thats what they were designed for. Nothing infurriates me more than seeing a function being created for something that is called only once.

Use a region.

If you need to do 500 lines then type the 500 lines in. If you want to neaten it up use a region, if there is anything reusable then use a function.

spinafex