2009-10-21 4 views
0

В Delphi я мог бы сделать что-то вроде этого:Как явно передать поток программы в блок finally в C#?

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

finally 
    DoSomethingElse; 
end; 

В других средств, если метод DoSomething результаты ложные, то поток программы transffered в конце концов блока и DoSomething2 и DoSomething3 не выполняются.

Как добиться такого поведения на 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.

+2

Прояснение для программистов на C# - в Delphi код в блоке finally всегда будет выполняться, даже если код вызывает «Exit» (эквивалент возврата). Я думаю, что Водзу хочет знать, так ли это на C#. –

+0

Да, Джерри, Точно. – Wodzu

+0

Вы хотите, чтобы ** DoSomethingElse ** выполнялся, если и только если любая из трех функций ** DoSomething ** возвращает false? – Mizipzor

ответ

14

Вы действительно не должны использовать конструкции обработки исключений для управления потоком. При этом Exit сопоставим с return в C#. Поскольку документация MSDN о ключе [ключевое слово [return]] [1] говорит:

Если оператор возврата находится внутри блока try, блок finally, если он существует, будет выполнен до того, как управление вернется к вызывающему методу.

В общем виде finally -блока почти всегда будет выполняться, если соответствующий try -блок был достигнут. Существует несколько редких ситуаций, когда невозможно гарантировать выполнение блока finally, но все они являются фатальными ошибками, по которым программы, вероятно, сразу же будут разбиты.

Как ваш код будет выглядеть в 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 -блок потому, что вы сделали return внутри try -блока.

+0

Я категорически не согласен с вами, но это был не мой вопрос. Кроме того, поведение «Выход» и «возврат» не идентично на этих языках, поэтому это ничего не дает. – Wodzu

+0

Joren спасибо вернуть таким образом, не компилируется. – Wodzu

+0

Я думаю, что вы отвечаете на мой вопрос перед редактированием? Мой обновленный ответ дает поведение, которое вы искали. Если оператор return находится в блоке try с блоком finally, блок finally будет выполнен до завершения метода. Но по-прежнему очень неправильно использовать try-finally для не исключительного управления потоком. – Joren

2

Почему бы не сделать три строки try обычным if/else-блоком? Вместо выхода вызовите DoSomethingElse. Как так:

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

Я хотел бы сказать, что «C# не Delphi», но это было бы немного высокомерным.

+2

+1 Для того, чтобы дать пример того, что * будет * быть достойным кодом. Большинство людей будут делать '! DoSomething()' вместо 'DoSomething() == false'. – Joren

+1

Спасибо mizipzor, я не хочу использовать такую ​​конструкцию, потому что она влияет на читаемость. Кроме того, я мог бы быть немного неясным, что я хочу также включить инструкцию catch. Если вы включите блок try..catch в свой пример, это будет еще больше влиять на читаемость, но я интересуюсь тем, как вы разрешите эту проблему? – Wodzu

+0

Почему вы хотите включить заявление catch и finally? Это чисто для удобочитаемости? Многие из ответов, на которые вы сосредоточили внимание на дискуссиях, должны быть использованы, если и только если бы работали с обработкой ошибок, а не для чтения или потока программ. Я настоятельно рекомендую вам обновить свой вопрос и указать, какую из трех проблем вы пытаетесь решить (обработка ошибок, читаемость, поток программы)! – Mizipzor

2

В C#, наконец, выполняется также, когда return вызывается внутри инструкции try.

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

Ответ на обновленный вопрос:

Причины у вас возникли проблемы, делая это в элегантном способе, потому что вы, кажется, используют комбинацию возвращаемых значений и исключений. Вы должны подумать о том, чтобы вручную создавать исключение вместо использования возвращаемых значений, если синтаксис является, ну, исключительным.

Предполагая, что есть хорошая причина для возвращения ценностей, однако, я думаю, это может быть понятнее идти без finally блока в целом, а также включить return в конце try блока, а также в вашем catch блоке , Это избавит вас от передачи сообщения об ошибке грязным способом.

Я не могу сказать, какое лучшее решение было бы, так как ваш фрагмент кода не показывает, что будет Response, если DoSomething() возвращает ненулевое значение.


Оригинальный ответ:

Это зависит немного от того, что вы пытаетесь достичь. Существуют ли исключения в любом из методов? В противном случае нет веской причины использовать шаблон try-finally. Это было бы эквивалентно (хотя, возможно, не желательно для удобства чтения):

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

Если есть исключения бросают, и обрабатываются на более высоком уровне, я бы рекомендовал изолировать этот код в отдельный метод, так что вы можете использовать a return заявление *.

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

     if (!DoSomething2()) 
      return; 

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

Чтобы ответить на ваш вопрос о том, когда блок finally кода выполняется: оно всегда выполняется, если исполняющего поток не завершается преждевременно.

*: Рекомендуется некоторая реструктуризация, поскольку нет эквивалента Delphi Exit. Оператор break ближе всего подходит, но его можно использовать только в конструкциях цикла или switch. Чтобы имитировать поведение выхода, вам понадобится goto и ярлык. Мы бы этого не хотели, не так ли? :)

+0

THorarin он может работать, когда возвращается void, но что, когда я хочу что-то вернуть из метода? Пожалуйста, взгляните на мой обновленный вопрос. – Wodzu

+0

Thorarin Я попробовал goto, но смог скомпилировать его. Я не против goto, поэтому, если бы вы могли предоставить такой пример, я был бы благодарен :) – Wodzu

+0

Какую проблему вы пытаетесь скомпилировать? У меня такое чувство, что оно напрямую не связано с самой «goto». – Thorarin

2

Что касается корпуса коммутатора, конечно, если вы не имеете в виду, наконец, в C#, сказав, наконец, блок.по умолчанию случай окончательно блокировать то, и вы также можете найти течь пример управления и здесь 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; 
    } 
} 
+0

+1 для интересного решения, но не могли бы вы привести пример, как он будет работать с поиском исключения? – Wodzu

+1

@ Проблема в том, что она не собирается выходить за исключение, но она похожа на код, который вы написали выше, поэтому я сказал, что вы можете использовать его. Но вы можете поместить этот код в блок catch try. Но в C#, чтобы управлять потоком программы, вы используете условия if-else и switch case. – Tarik

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 
} 

EDIT - отслеживание изменившихся требование

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 
} 
+0

Спасибо Стив. Я думаю, что это очень близко к тому, что я хочу получить. Не могли бы вы расширить свой пример, чтобы включить обработчик исключений, как в коде, который я представил в моем обновленном вопросе? – Wodzu

+0

Расширено в соответствии с запросом, отметив, что я только хочу обрабатывать какой-либо тип исключения (в противном случае, если нет разумной общей базы, поймайте любое исключение, а если оно не относится к типам, которое вы хотите, тогда бросьте, иначе как просто извлеките сообщение. –

+0

Спасибо Стиву. Я нахожу это красивым и чистым. +1 С небольшими изменениями (сообщение также зависит от результата DoSomething) я могу добиться ожидаемого поведения. Что вы думаете о моем решении? – Wodzu

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

Im все еще борется с тем, как, в хорошем смысле, положить finally в это.

+0

Спасибо mizipzor за то, что вы потратили свое время на это. что ответ должен быть возвращен в каждом случае.Если один из DoSomethings равен 0, причина должна быть задана в ответе. Если все DoSomething переданы, то Response должен содержать информацию, которую они передали. достаточно заменить нуль в вашем примере «вернуть новый XElement (« SomeTag »,« SomeResponse »). Однако, как вы указали, нет окончательного блока ... – Wodzu

+0

Если функция вернет 0, вместо этого выведите исключение. С помощью этой настройки он будет пойман и будет передана соответствующая информация. – Mizipzor

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(); 
} 
+0

Спасибо JonB за пример. Да, я хотел избежать вложенных, потому что IMHO делает код сложнее читать. Все мои вопросы касаются управления потоком программы для повышения удобочитаемости. Я администратор, что на вашем примере это выглядит очень ясно, но когда вам придется настраивать Response после каждого из DoSomethings, он станет беспорядочным. – Wodzu

+0

Добавил пару других возможностей, я думаю, что цикл может быть самым аккуратным. – 2009-10-22 09:44:36

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

Что вы думаете?

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

    if (!DoSomething2()) 
     return; 

    if (!DoSomething3()) 
     return; 
} 

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