catch()-блок как направление потока (вроде)

В настоящее время я использую такие блоки кода в своем проекте

     public ActionStatus GetStuff(out output)
     {
        try
        {
            if(!validation)
              return ActionStatus.DataInvalid;
            //do possible error stuff here
            output = data;
            return ActionStatus.Successful;
        }
        catch
        {
            output = null;
            return ActionStatus.Failed;
        }
     }

Я использую это, поскольку перечисления ActionStatus достаточно для продолжения программы (в большинстве случаев это чтение данных из кэша или базы данных), поскольку перечисление предоставляет достаточно информации, чтобы предоставить сообщение пользователю. Эти методы будут иметь атрибут PostSharp-Weaver для регистрации сообщения для последующего использования при отладке.

На мой взгляд, это нормальный способ обработки таких некритических исключений, поскольку перечисления достаточно для продолжения; с другой стороны, у меня всегда есть поговорка «не управляйте потоком вашей программы с помощью исключений», но я интерпретирую это как использование

if(ex == fooException)
  doThis();
else if (ex == barException)
  doThat();

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

ОБНОВЛЕНИЕ: Из двух ответов я вижу, что необходимо дополнительное разъяснение, особенно в отношении PostSharp-weaver.

PostSharp — это фреймворк аспектно-ориентированного программирования (АОП), он в основном позволяет вам создавать классы, которые перезаписывают определенные методы, такие как OnEnter, OnExit, OnException и т. д. должны поместить атрибут в метод или класс, который вы хотите изменить во время компиляции. При этом я не потеряю информацию об исключениях, так как стек вызовов и все остальное останется прежним.

ОБНОВЛЕНИЕ 2: пропущено назначение, необходимо назначить выходные параметры.


person Femaref    schedule 16.08.2009    source источник
comment
Опасность использования вашего подхода заключается в том, что люди, использующие ваш метод на более высоком уровне, могут (и это происходит довольно часто) просто игнорировать ваш результат. Игнорировать исключение (немного) сложнее, поэтому: * вернуть вывод * выдать исключение при проблемах   -  person mfloryan    schedule 17.08.2009
comment
Я пропустил присваивание в коде, так как выходные параметры должны быть присвоены перед выходом из метода. По сути, они не могут игнорировать это, так как нуль в любом случае просто сломает их код.   -  person Femaref    schedule 17.08.2009


Ответы (2)


Обновление на основе обновления OP (структура AOP)

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

Однако в этом случае я прибегаю к совету @silky из комментария, который он оставил здесь, и к предостерегающей записке от @mfx по самому вопросу: зачем вообще ловить? Дизайн вашего программного обеспечения основан на более высоких уровнях проверки возвращаемого значения из вашего метода. Разработчикам очень легко это пропустить или забыть сделать. По крайней мере, если исключение всплывает в стеке и возникает, его можно отметить при тестировании и настроить код. Если возвращаемое значение не проверяется, ошибки могут стать значительно более коварными.

Опять же, я указываю вам на рекомендации Microsoft по обработке исключений для разработчиков. (В аннотированной форме книги, которую я очень рекомендую, есть много заметок о том, почему были приняты проектные решения, в том числе заметки о том, почему они решили — обычно — уклоняться от кодов возврата в пользу исключений.)

Что касается вашего (обновленного) кода, почему это не может быть приемлемым редизайном?

 public WhateverTypeOutputWas GetStuff()
 {
    if(!validation)
        return ActionStatus.DataInvalid;
    //do possible error stuff here
    return data;
 }

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

Судя по тому, как код был написан в вопросе, разработчик получит исключение NullReferenceException, если забудет проверить ваш код состояния. В таком случае, зачем вообще нужен код состояния? Может ли разработчик с уверенностью предположить, что ненулевой возврат любого output означал успешный запуск? А в приведенном выше дизайне мы вырезаем половину метода, правильно всплываем исключения, правильно используем возвращаемые значения (в соответствии с рекомендациями Microsoft по разработке фреймворка) и упрощаем жизнь разработчику, использующему этот метод. Беспроигрышный.

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

Зачем вам удалять ценную информацию, содержащуюся в исключении, которое вы перехватываете? Повторить это:

catch(Exception ex)
{
    // Any handling (like logging, etc.) here
    // If no handling, don't bother catching
    throw;
}

и позволить коду более высокого уровня улавливать и определять, что отображать пользователю (если что-то) и (надеюсь) регистрировать подробную информацию в случае необходимости.

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

Что касается исключений как потока управления, я не думаю, что следующее (из вашего исходного вопроса) будет считаться следующим:

if(ex == fooException)
  doThis();
else if (ex == barException)
  doThat();

Я думаю, что рекомендации по проектированию, касающиеся исключений как потока управления, на самом деле относятся к генерации исключений как к способу раннего выхода из циклов (вместо использования break, EG), использованию исключений, когда в коде ничего не пошло не так, как простым ранним возвратам и т. д.

Я указываю вам на официальные рекомендации по разработке исключений от Microsoft; поскольку именно этому я стараюсь следовать в ходе своей работы, и я ожидаю, что разработчики, работающие со мной, будут делать то же самое. (То есть, если у нас нет действительно веских документально подтвержденных причин не делать этого. И опять же, такие случаи редки.)

Помните, что исключения предназначены для исключительных событий в вашем коде — обычно, например, для ошибок. Есть причина, по которой мы не должны проверять коды успеха/неудачи в библиотеке базовых классов .NET — предпочтительная модель в .NET — генерировать исключение, если метод не работает. Таким образом, обработка ошибок относится к try/catch, а коды возврата используются для возврата полезных данных клиентскому коду, использующему ваши методы.

person John Rudy    schedule 16.08.2009
comment
Зачем даже ловить его, если все, что вы делаете, это повторно бросаете его? - person Noon Silk; 17.08.2009
comment
@silky: Технически нет необходимости, правильно. Это был образец, а не фактический код. В своих нижних слоях я редко ловлю именно по этой причине, а по мере приближения слоев к чему-то полезному, в точке лова есть место для логирования. В любом случае, коды возврата не подходят для .NET. - person John Rudy; 17.08.2009
comment
@silky: И я изменил его в соответствии с вашим комментарием. - person John Rudy; 17.08.2009
comment
Дело в том, что я не вижу необходимости для вызывающего абонента видеть, ПОЧЕМУ это не удалось, если отсутствует файл или произошла ошибка синтаксического анализа, это просто не имеет значения. Как и в случае использования параметра out, переменная в любом случае будет нулевой, поэтому, тем не менее, БУДЕТ исключение с возникновением ошибки, потому что в ту же секунду, когда он попытается использовать эту переменную, произойдет исключение NullReferenceException. - person Femaref; 17.08.2009
comment
Дизайн все же ненормальный с точки зрения .NET. - person John Rudy; 17.08.2009

Я думаю, что ваша модель вполне разумна.

И утверждение «не управляйте потоком вашей программы с помощью исключений» немного странно, потому что это именно то, что вы должны делать. Если вы просто продолжаете вести себя так, как будто исключения никогда не было, то я не могу представить, что вы делаете! :)

И я также согласен с тем, что ваше предложение об утверждении «если» довольно нелепо, и любой, кто делает это, должен быть расстрелян.

Но, конечно, что-то вроде

try {
}
catch(SomeException e) { }
catch(Exception e){ }

Разумно.

-- Редактировать:

Обновите, чтобы подтвердить, что я не вижу никакого отношения к этому вашему AOP; это вполне разумно, ИМХО, несмотря ни на что.

person Noon Silk    schedule 16.08.2009
comment
Причина продолжения, как если бы исключения никогда не было, заключается в том, что если исключение произошло, ваш код переноса не будет выполняться, так как стек раскручивается. - person John Saunders; 17.08.2009
comment
ну, я имел в виду не использовать Exception() (или любой производный класс) для управления вашим потоком, поэтому в основном бросайте исключения как способ управления потоком при любом шансе. - person Femaref; 17.08.2009
comment
Но я хочу сказать, что исключения «контролируют» поток, заставляя вас делать что-то еще. Очевидно, что с ними можно делать глупости; просто не делай этого. - person Noon Silk; 17.08.2009
comment
да, я имел в виду глупые вещи, и даже с моим ограниченным опытом (около четырех лет, мне сейчас 18) я видел вещи, о которых я просто могу фейспалмить. - person Femaref; 17.08.2009
comment
К вашему редактированию: AOP в основном добавляет (упрощенное объяснение) Logger.Log(e); в блок catch (или любой другой материал, который вы хотите;), поэтому вам просто нужно добавить атрибут ExceptionLogger() в начало метода или класса. В основном заботится о регистрации ошибок, добавляя определенные операторы в определенные места кода во время компиляции. - person Femaref; 17.08.2009
comment
:nod: Я знаком с АОП :) - person Noon Silk; 17.08.2009
comment
не прозвучало, мои извинения. - person Femaref; 17.08.2009