开发者

#region/#endregion vs sub functions?

开发者 https://www.devze.com 2022-12-09 14:25 出处:网络
How do you recommend us开发者_Go百科ing #region / #endregion? To what extent should that replace using sub functions to clarify your code?Not at all.

How do you recommend us开发者_Go百科ing #region / #endregion? To what extent should that replace using sub functions to clarify your code?


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.


#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.


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.


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.


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


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号