0

在德尔福我可以做这样的事情:

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

finally
  DoSomethingElse;
end;

换句话说,如果方法DoSomething结果为假,则程序流被转移到 finally 块并且DoSomething2DoSomething3被执行。

如何在 C# 中实现这种行为?

提前致谢。

Edit1: 下面的示例在 VS 2008 中无法编译

Edit2:对不起,我太快忘记了退货声明;

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: 经过测试,似乎实现这一目标的最简单方法是如果结果DoSomething1为假则抛出异常。我可以抛出我自己的执行,写一个特定的消息并将它传递给 finally 子句。

4

10 回答 10

14

你真的不应该使用异常处理结构来进行流控制。也就是说,ExitreturnC# 相当。正如关于 [return 关键字][1] 的 MSDN 文档所说:

如果 return 语句在 try 块内,则 finally 块(如果存在)将在控制返回调用方法之前执行。

通常,如果已到达finally相应的-block,则几乎总是会执行-block。try在极少数情况下,无法保证finally-block 执行,但它们都是致命错误,程序可能会立即崩溃。

您的代码在 C# 中的外观:

try
{
    if (!DoSomething())
        return;

    if (!DoSomething2())
        return;

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

但同样,不要这样做。try并且finally用于处理异常,而不是用于正常的流控制。

回复您的编辑:
在您的代码return中无法编译,因为方法的返回类型是XElement并且return它本身只能在返回类型为void. 您可以使用return new XElement("SomeTag", "SomeResponse");, 因为finally无论如何都会这样做,或者您可以Response更早地分配并执行return Response;.

请注意,虽然finally总是执行,return Response;但如果进入finally-block 的原因是因为您return在 -block 内部执行了 a ,则之后的 that 不会执行try

于 2009-10-21T08:06:25.850 回答
3

回答更新的问题:

您无法以优雅的方式执行此操作的原因是因为您似乎正在使用返回值和异常的组合。如果位置是异常的,您应该考虑手动引发异常而不是使用返回值。

但是,假设返回值有充分的理由,我认为完全不使用块并在块的末尾和块finally中包含 a可能会更清楚。这将使您免于以混乱的方式传递异常消息。returntrycatch

我真的不能说最好的解决方案是什么,因为您的代码片段没有显示Response如果 DoSomething() 返回非零值会是什么。


原答案:

这在一定程度上取决于您要完成的工作。是否真的在任何方法中引发了异常?否则没有充分的理由使用try-finally模式。这将是等效的(尽管可能不建议可读性):

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

如果抛出异常并在更高级别进行处理,我建议将此代码隔离在单独的方法中,以便您可以使用return语句*。

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

        if (!DoSomething2())
            return;

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

回答关于何时执行finally代码块的问题:它总是被执行,除非执行线程过早终止。

*:建议进行一些重组,因为没有与 Delphi 等效的东西Exit。该break语句最接近,但它只能用于循环构造或switch块中。要模仿退出行为,您需要goto一个标签。我们不希望那样,现在可以吗?:)

于 2009-10-21T08:19:54.530 回答
2

为什么不将三个 try 行作为一个通用的 if/else 块呢?而不是退出,调用 DoSomethingElse。像这样:

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

我想说“C#不是Delphi”,但这有点自大。

于 2009-10-21T08:08:26.843 回答
2

如果你不是说finally块,那么switch case当然会怎么样。默认情况是 finally 块,您还可以在 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;
    }
}
于 2009-10-21T08:31:02.637 回答
2

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");
        }
于 2009-10-21T08:14:22.963 回答
1

在这种情况下,将问题理解为专门处理非异常处理情况,我会将 的内容重构try为私有辅助方法,如下所示

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
}

编辑——跟踪更改的需求

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
}
于 2009-10-21T08:34:45.357 回答
0

使用更新的信息对此进行另一次破解:

我希望 DoSomethingElse 始终被执行,并且我希望它包含来自可能异常的消息

如果任何 DoSomething 的返回 0,则返回 null。如果不是,则创建通用消息。如果有异常,它会被捕获并返回一条带有其信息的消息。这个怎么样?

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);
  }
}

我仍在努力解决如何以一种好的方式投入其中finally

于 2009-10-21T10:47:16.593 回答
0

我发现它的行为与我一开始展示的 Delphi 非常相似。我对你的评论很感兴趣。Response取决于 DoSomethings 结果。

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;
}

你怎么看?

于 2009-10-21T17:38:03.713 回答
0

这似乎复制了德尔福: -

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

另一种风格(有些人会讨厌这种风格,有些人会喜欢它。):-

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

我得到的印象是你想要一些其他的行为吗?

转到版本?

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

  if (!DoSomething2())
    goto Exit;

  if (!DoSomething3())
    goto Exit;

Exit:;
}
finally
{
  DoSomethingElse();
}

注意刺激性;在标签之后,似乎标签必须在声明之前。

刚刚顿悟:-

Func<bool>[] somethings = new Func<bool>[] {DoSomething, DoSomething2, DoSomething3};
try
{
  foreach (Func<bool> something in somethings)
  {
    if (!something())
      break;
  }
}
finally
{
  DoSomethingElse();
}
于 2009-10-21T14:39:13.187 回答
0
void funcA()
{
    if (!DoSomething())
        return;

    if (!DoSomething2())
        return;

    if (!DoSomething3())
        return;
}

void funcB()
{
    funcA();
    DoSomethingElse;
}
于 2009-10-21T17:54:38.953 回答