views:

708

answers:

6

I've been programming in C# for...a while now. I coded a routine recently and it occurred to me that it was the first time (that I could recall) that I deliberately used bare code blocks (i.e. with no preceding control-flow statement). The code looks vaguely like this:

//...

var output = source.GetRawOutput();

{
    var fooItems = FooSource.GetItems();

    output = TransformA.TransformOutput(output, p =>
        {
            GetFooContent(p, fooItems.GetNext());
        });
}

{
    var barItems = BarSource.GetItems();

    output = TransformB.TransformOutput(output, p =>
        {
            GetBarContent(p, barItems.GetNext());
        });
}

return output;

I structured the code this way mostly as a sanity check that I wasn't going to accidentally reference the wrong variable (i.e. mix up barItems and fooItems). I also find the code a bit more readable. I certainly could have factored the code into three separate methods, but I felt that was a bit overkill in this case.

Do you use bare code blocks in your code? Why or why not?

+6  A: 

If you look at your code, those two blocks are very similar. I bet they can be refactored into a single block.

Although that doesn't sound like an answer, it is. I think in general if you feel the desire to use braces like that, you are dealing with a situation that would be better handled by factoring out another method or refactoring pieces together.

In general anyway.

Specific answer--no, once I got good at OO and limiting how much work I do in a single unit, I've never thought they might be a good idea.

Edit: With code that similar, it MUST be fairly easily refactorable. I'll try a refactor. Sorry if I get the syntax wrong, I really don't do c# and Java doesn't have closures.

output = transform(FooSource, TransformA);
output = transform(BarSource, TransformB);  // I know output is overwritten, but
                // it is in the askers' example as well

transform(var itmSource, var transform) {
    var output=source.GetRawOutput();  // Sorry, you never said where source came from.

    var items = itmSource.GetItems();
    output=transform.TransformOutput(output, p =>
        {
            GetContent(p, items.GetNext()); // GetContent may need to be passed in
                                    // you didn't say where those calls came from.
                                 // See comments below
        });
    }
    return output;
}

Refactors like this don't save much typing, but they show up some great patterns--such as the relationship between FooSource and TransformA (and possibly the getContent call)--there is a decent chance that they should be stored in a single object and that object should be passed in, or something similar. (It's hard to tell from this fragment, often refactoring requires a much wider view of the code than you gave)

Note that they also force you to think about GetFooContent and GetBarContent. I'd bet you a pint of beer that these are so similar that they could either be factored into a single method call with a variable or two passed in or into a method in two sibling classes.

Because of the way this kind of relationship always shows up and ALWAYS improves other code in your classes, I believe these kind of refactors are absolutely mandatory, doing this kind of refactoring more than anything taught me real OO.

Bill K
Thanks for the response, Bill. As it happens, the "real" code presents more of a challenge to refactoring than my example code. It could almost certainly be factored further, but I'm not convinced it passes the cost/benefit test.
Daniel Pratt
I've updated my example code to bring it closer to the structure of the real code.
Daniel Pratt
You can almost always identify code that needs refactoring by squinting until you can't read the letters and look if you see repeated visual patterns. I wish I was better at C# syntax, maybe someone could refactor this for you into a single method that is called twice?
Bill K
First, an explanation: The source of the first transform is the raw output. The source of the second transform is the output of the first transform.
Daniel Pratt
Second, I know you're answer is not merely for my benefit, but...please trust me that I am not one who needs to be convinced of the benefits of refactoring :)
Daniel Pratt
So you need to pass "Ohtput" in the second time you call it. And I thought you might need convincing when you said: "But I'm not convinced it passes the cost/benefit test." I'm guessing you don't see as much benefit, so I tried to point out once you may have missed, but point taken.
Bill K
+2  A: 

Personally, I don't do that for structuring code. This is used in C++ to control the scope of variables and call the destructors but as C# is garbage collected, I don't see a functional usage of blocks.

Regarding structuring for readability, I believe your method is either small enough that doesn't need these kind of stuff or it should probably be broken up into smaller methods.

Mehrdad Afshari
+1  A: 

I find that, most of the time I'm tempted to do this, I'm better off refactoring it into a separate method. Take your example - in this case, it appears you could use a single method that would handle both of your bare code blocks with the correct parameters.

Refactoring that would make it more clear, and easier to maintain.

Reed Copsey
+1  A: 

I never use them, instead I tend to put blocks of code into smaller methods, with descriptive names, this have the added bonus of making the intend clearer.

I'd like to add though in the example you provide it the two blocks see rather similar it might be a change for you to replace them both with two method calls, to a single method that does the work for you like:

Arkain
A: 

I use them to wrap temporary variables. For example, sometimes its necessary to create an object, set a few of its variables, pass it into a function, and dispose of it.

Mark
Wouldn't you use 'using' for that?
Will Dean
Uh... probably. I come from a C++ background, where that's not available AFAIK. I'm really not sure what the difference is anyway... either way, they terminate at the end of the block, no?
Mark
A: 

I almost never use them, like others have said. There is one edge case where you might use them: the enclosing block is what determines how specific the compiler will be about capturing variables into closures.

Charlie Flowers