Wodzu
Wodzu

Reputation: 6989

How to explicitly pass a program flow into the finally block in C#?

In Delphi I could do something like this:

try
  if not DoSomething then
    Exit;
  if not DoSomething2 then
    Exit;
  if not DoSomething3 then
    Exit;

finally
  DoSomethingElse;
end;

In other means if method DoSomething results false then the program flow is transffered to the finally block and DoSomething2 and DoSomething3 are not executed.

How to achieve such behaviour in C#?

Thanks in advance.

Edit1: The below example doesn't compile in VS 2008

Edit2: I am sorry I was to fast and forget the return statement;

XElement OrderStatus(q_order_status Request)
{
  XElement Response;
  try
  { 
    if (DoSomething() != 0 )
    {
      return;
    }
  }
  catch(Exception e)
  {
    // catch some errors and eventually pass the e.Message to the Response
  }
  finally
  {
    Response =  new XElement("SomeTag", "SomeResponse");
  }
  return Response;
}

Edit3: After testing it seems that the easiest way to achieve this is to throw an exception if the result of DoSomething1 is false. I can throw my own execption, write a specific message and pass it to the finally clause.

Upvotes: 0

Views: 2702

Answers (10)

Tarik
Tarik

Reputation: 81781

What about switch case of course If you don't mean the finally in c# by saying finally block. default case is the finally block then and you can also find flow control example and here at msdn : Flow Control (C# vs. Java)

static void Main(string[] args)
{
    switch (args[0])
    {
        case "copy":
            //...
            break;

        case "move":
            //...
            goto case "delete";

        case "del":
        case "remove":
        case "delete":
            //...
            break;

        default:
            //...
            break;
    }
}

Upvotes: 2

user159335
user159335

Reputation:

This appears to replicate the delphi:-

try
{
  if(DoSomething())
    if(DoSomething2())
      DoSomething3();
}
finally
{
  DoSomethingElse();
}

an alternate style (some people will hate this style, others will love it.):-

try
{
  DoSomething() && DoSomething2() && DoSomething3();
}
finally
{
  DoSomethingElse();
}

I get the impression you want some other behaviour though?

Goto version?

try
{
  if (!DoSomething())
    goto Exit;

  if (!DoSomething2())
    goto Exit;

  if (!DoSomething3())
    goto Exit;

Exit:;
}
finally
{
  DoSomethingElse();
}

Note the irritating ; after the label, it seems a label must precede a statement.

Just had an epiphany:-

Func<bool>[] somethings = new Func<bool>[] {DoSomething, DoSomething2, DoSomething3};
try
{
  foreach (Func<bool> something in somethings)
  {
    if (!something())
      break;
  }
}
finally
{
  DoSomethingElse();
}

Upvotes: 0

Brian
Brian

Reputation: 25834

void funcA()
{
    if (!DoSomething())
        return;

    if (!DoSomething2())
        return;

    if (!DoSomething3())
        return;
}

void funcB()
{
    funcA();
    DoSomethingElse;
}

Upvotes: 0

Wodzu
Wodzu

Reputation: 6989

I find it quite similar in behavior to the Delphi's one which I have shown on the beginning. I am interested in your comments. Response is dependent on the DoSomethings result.

XElement OrderStatus(q_order_status Request)
{
  XElement Response;
  int result = 0;
  string Message = "";
  try
  { 
    result = DoSomething1();
    if (result != 0)
    {
      throw new DoSomethingException("DoSomething1 has failed!");
    }
    result = DoSomething2();
    if (result != 0)
    {
      throw new DoSomethingException("DoSomething2 has failed!");
    }
    result = DoSomething3();
    if (result != 0)
    {
      throw new DoSomethingException("DoSomething3 has failed!");
    }
    Message = "All tests has been passed.";
  }
  catch(DoSomethingException e)
  {
    Message = e.Message;
  }
  catch(Exception e)
  {
    Message = e.Message;
  }
  finally
  {
    Response =  new XElement("SomeTag", Message);
  }
  return Response;
}

What do you think?

Upvotes: 0

Steve Gilham
Steve Gilham

Reputation: 11277

In this sort of situation, understanding the question as dealing exclusively with the non-exception handling case, I would refactor the contents of the try into a private helper method, like this

void BranchOnContext()
{
        if (!DoSomething())
            return;

        if (!DoSomething2())
            return;

        // last one will drop out and return anyway
        DoSomething3();
}

void DoStuff()
{
    BranchOnContext();  // Assumed not to throw
    DoSomethingElse();  // Always the next thing to be executed
}

EDIT -- tracking the changed requirement

void DoStuff()
{
    string message = string.Empty;
    try {
        BranchOnContext();
    } catch (MyExpectedException me) { // only catch exceptions I'm prepared to handle
         message = me.Message;
    }  
    DoSomethingElse(message);  // Always the next thing to be executed
}

Upvotes: 1

Mizipzor
Mizipzor

Reputation: 52381

Taking another crack at this with the updated info:

I want DoSomethingElse to be executed always and I want it to include message from possible exception

If any of the DoSomething's return 0, null is returned. If not, the generic message is created. If there was an exception, it is caught and a message with its info is returned. How about this?

XElement OrderStatus(q_order_status Request)
{
  try
  { 
    if (DoSomething() != 0 )
    {
      return null;
    }
    else
    {
      return new XElement("SomeTag", "SomeResponse");
    }
  }
  catch(Exception e)
  {
    // catch some errors and eventually pass the e.Message to the Response
    return new XElement(e.tag, e.response);
  }
}

Im still struggling with how to, in a good way, put finally into this.

Upvotes: 0

Thorarin
Thorarin

Reputation: 48496

Answer to updated question:

The reason you're having trouble doing this in an elegant way, is because you seem to be using a combination of return values and exceptions. You should consider manually raising an exception instead of using return values if the sitation is, well, exceptional.

Assuming there is a good reason for the return values however, I'm thinking it might be clearer to go without a finally block altogether, and to include a return at the end of the try block and also in your catch block. That would save you from passing the exception message in a messy way.

I can't really say what the best solution would be, since your code snippet does not show what Response would be if DoSomething() returns a non-zero value.


Original answer:

It depends a little on what you're trying to accomplish. Are exceptions actually being thrown in any of the methods? Otherwise there is no good reason to use a try-finally pattern. This would be equivalent (though maybe not advisable for readability):

bool doneEverything = DoSomething() && DoSomething2() && DoSomething3();
DoSomethingElse();

If there are exceptions being thrown, and handled at a higher level, I'd recommend isolating this code in a separate method, so you can use a return statement*.

void DoStuff()
{
    try
    {
        if (!DoSomething())
            return;

        if (!DoSomething2())
            return;

        if (!DoSomething3())
            return;
    }
    finally
    {
        DoSomethingElse();
    }
}

To answer your question about when the finally code block is executed: it is always executed, unless the executing thread terminates prematurely.

*: Some restructuring is recommended, because there is no equivalent of the Delphi Exit. The break statement comes closest, but it can only be used in loop constructs or switch blocks. To mimic Exit behavior, you would need goto and a label. We wouldn't want that, now would we? :)

Upvotes: 3

Joren
Joren

Reputation: 14746

You really shouldn't be using exception handling constructs for flow control. That said, Exit is comparable to return in C#. As the MSDN Documentation about the [return keyword][1] says:

If the return statement is inside a try block, the finally block, if one exists, will be executed before control returns to the calling method.

In general a finally-block will almost always execute if the corresponding try-block has been reached. There are a few rare situations where it is impossible to guarantee that the finally-block executes, but they are all fatal errors, upon which programs should likely immediately crash.

How your code would look in C#:

try
{
    if (!DoSomething())
        return;

    if (!DoSomething2())
        return;

    if (!DoSomething3())
        return;
}
finally
{
    DoSomethingElse();
}

But again, don't do this. try and finally are intended for handling exceptions, not for normal flow control.

Reply to your edit:
In your code return doesn't compile because the return type of the method is XElement and return by itself can only be used when the return type is void. You could use return new XElement("SomeTag", "SomeResponse");, as that is what the finally would be doing anyway, or you could assign Response earlier and do return Response;.

Note though that while the finally always executes, the return Response; that comes after it doesn't execute if the reason went into the finally-block is because you did a return inside the try-block.

Upvotes: 15

Frederik Gheysels
Frederik Gheysels

Reputation: 56964

In C#, finally is executed as well when return is called inside the try statement.

bool doSomething = false; bool doSomething2 = true;

        try
        {
            if( !doSomething )
            {
                Console.WriteLine ("not dosomething");
                return;
            }
            if( !doSomething2 )
            {
                Console.WriteLine ("not dosomething 2");
                return;
            }
        }
        finally
        {
            Console.WriteLine ("In finally");
        }

Upvotes: 2

Mizipzor
Mizipzor

Reputation: 52381

Why not make the three try-lines a common if/else block? Instead of exit, call the DoSomethingElse. Like so:

if (DoSomething() == false)
{   
    DoSomethingElse();
}
else if (DoSomething2() == false)
{
    DoSomethingElse();
}
else if (DoSomething3() == false)
{
    DoSomethingElse();
}

I would like to say that "C# is not Delphi", but that would be a bit arrogant.

Upvotes: 2

Related Questions