David Laribee wrote a few days ago about why he dislikes the #region construct in code. Interesting comments, and made me reflect a bit on how I use regions myself. In general, I don't agree with David that using regions is evil per-se. but I do agree they are often overused. In particular, I find the practice of forcing a specific set of regions on each and every code base to be a significant hindrance to readability.
The truth is that small classes don't usually need regions at all. However, I still find them very convenient for classes with a lot of members, even if they are short 1 or 2 liners. What I noticed, however, is that I don't have a hard and fast rule about how I apply regions. Instead I sort of vary my use of them according to the needs of the code I'm working on.
Here are some of the things I keep in mind when applying regions:
Lots of properties
If a class has a lot of properties (common, for example, in some business entities or DTOs), then I do like to wrap properties in it's own region. This is because a lot of cases properties are created once, but not so often modified after wards, and the other thing is that property declarations in C# do take up a lot of space because of the syntax used.
Automatic properties in C# will be nice, but from a syntax perspective, they don't really help as much as one would like. It was unfortunate the C# team decided to go for that syntax instead of the really nice one used by C++/CLI...
Multiple Interfaces Implemented
If a class implements multiple interfaces, each one having more than a couple of methods, I do tend to wrap each one into its own region. If it's only a single interface, or something simple like IDisposable, I don't bother with it.
Lots of Related Methods
Sometimes you have to implement a lot of simple, related methods in a class. We all wish classes had few members and were small and simple, but sometimes you have to do otherwise (this is usually forced when complying with an externally defined interface).
For example, in a lot of cases I've had to deal with there are a lot of simple GetXXX() accessor methods that need to be implemented (e.g. GetInt16(), GetInt32() and so on). Each one usually won't have more than 1 or 2 lines of code in it, but when put together, they take a sizable chunk of space, and if there are other, more significant methods in the class, I like to get the accessors out of the way so I can concentrate on the rest (the important stuff). Regions help out nicely in this case.
Related Test Methods
A case similar to the above one happens in Unit Testing classes: Sometimes you have several test methods that are closely related to one another, so I like to group them together. Here are a couple of examples:
- I once worked in a code base that, although fairly simple, had tons of methods in a single class: a combination of type-specific Read/Write methods. For each type, I usually had to write four or five test methods to test all possible corner cases, so that meant a hole lot of methods involved. I grouped test methods by type (i.e. "Int32 tests") inside the fixture.
- I'm currently working on an ADO.NET provider, and writing/testing my IDataReader implementation. The IDataReader class has tons of methods (of the accessor variety mentioned before), which, means lots of test methods in my test fixture. Again I use regions to group certain related tests methods together to make it easier to read and maintain.
There might be a couple of more cases I base my regions on, but I think these cover the basics. I think, in general, these rules give me a set of regions that are far more useful and that really make the code easier to read and maintain than the rigid Using/Fields/Properties/Public Methods/Private Methods stuff a lot of people use; but that's just my opinion :-).
Coincidentally, like David, I use search a lot to go around (though in my case I use '/' for searching and not Ctrl-I), and the other thing I've found is that tend to very rarely use the class/member code navigation drop downs in the Visual Studio Editor. I do use them sometimes, but usually when I'm just getting into a foreign code base I'm not familiar with.