Подтвердить что ты не робот

Неправильная ли практика возврата Исключения из ваших методов

Многие методы базы кода, написанные моими сверстниками, выполняют свою собственную обработку ошибок, как правило, путем поиска, уведомления и ведения журнала.

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

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

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

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

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

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

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

Это имеет смысл? Может быть, я не использовал лучший пример, , но, вообще, нормально ли возвращать Исключения, или там лучший образец там?

UPDATE

вау, подавляющий результат никак. Я так думала. Я должен сказать, что делать это (возвращая исключение) kinda решил проблему, но он чувствовал себя не так.

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

UPDATE:

Я очень ценю все мнения, я читаю все, и я тщательно обдумываю все материалы.

В настоящее время я предпочитаю иметь метод void, бросая Исключения, а затем ловить их снаружи... это лучше?

4b9b3361

Ответ 1

Если вы имеете в виду что-то вроде...

public Exception MyMethod( string foo )
{
   if( String.IsNullOrEmpty() )
   {
      return new ArgumentNullException( "foo" );
   }
}

... а не...

public void MyMethod( string foo )
{
   if( String.IsNullOrEmpty() )
   {
      throw new ArgumentNullException( "foo" )
   }
}

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

Еще одна веская причина - не то, что стандартные делегаты больше не будут соответствовать вашей сигнатуре метода. Так, например, вы больше не могли бы использовать Action<string> в MyMethod и вместо этого использовать Func<string,Exception>.

@Andy, за комментарий, ответ слишком долго для комментария: Не совсем. Не беспокойтесь о вызывающем. В любом случае, он должен быть защитным в своем дизайне. Подумайте о семантике исключения... "Выполнение этого приложения прекратится прямо здесь, если кто-то не знает, что делать с этой проблемой". Если вы решите проблему, вы должны ее решить. Если вы не можете, вы должны занести его в журнал и вывести его на следующий уровень, потому что они могут точно знать, что делать.

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

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

Проверьте Блок обработки исключений Enterprise Library. Он считает, что они действительно формулируют большое видение того, как справляться с исключениями в вашей архитектуре.

Ответ 2

Никто не сказал этого: я понимаю, что в .NET трассировка стека исключений не заполняется до тех пор, пока исключение не будет брошено. См. Рекомендации по обработке исключений, где говорится:

  • Трассировка стека начинается с оператора, в котором исключение вызывается и заканчивается в выводе catch, которое ловит исключение. Имейте в виду этот факт, когда вы решаете, где разместить инструкцию throw.

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

В .NET, если вы просто создаете экземпляр Exception и возвращаете его, у вас не будет никакой информации о стеках в этом исключении. Это преимущество, когда вы используете эту функцию для создания метода создания исключений, потеря, когда вы используете Исключения так, как вы спрашиваете. Для сравнения, в Java информация о стеке добавляется при создании экземпляра Exception, независимо от того, была ли она когда-либо выбрана.

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

Накладные расходы на производительность брошенного Exception достаточно малы в любой современной JVM и в любой недавней версии .NET. Возвращение исключений, а не бросание их - по соображениям производительности - является неуместной оптимизацией... , если, вы не профилировали свою программу и не обнаружили, что определенный бросок действительно является узким местом, и что возврат Исключение имеет смысл. (Что меня удивило бы, но все возможно.)

Примечание. Предположим, вы создали метод, который в противном случае был бы void, который возвращает исключение при ошибке. Это означает, что если вы хотите проверить наличие ошибки, вам нужно проверить с помощью null, чтобы увидеть, была ли ошибка или нет. Как правило, методы, возвращающие значение null на ошибку, способствуют хрупкому коду. Например, метод, возвращающий список, должен возвращать пустой список, а не null.

Также обратите внимание, что в .NET при повторном выполнении исключения, поскольку стек заполняется в throw, вы обычно теряете исходную трассировку стека. Чтобы этого избежать, используйте bare throw без указания экземпляра Exception. Если вы throw ex, то вы потеряете исходную трассировку стека.

Ответ 3

Вы никогда не должны возвращать исключения. Вместо этого, если ваша функция достаточно сложна, создайте класс, являющийся результатом вашей функции. Например, метод GetPatientList возвращает GetPatientListResult. Он мог бы стать членом "Успеха". Исключения для действительно необычных вещей, таких как исчезновение файла в середине операции, неверные или нулевые параметры, заданные для функции, база данных идет вниз. Кроме того, он разбивает всю модель абстракции - Dog.GoForAWalk должен возвращать DogWalkReport, а не PossiblyNullDogWalkException.

Ответ 4

Нет.

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

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

Ответ 5

Наиболее определенно неортодоксальное использование исключений. Функция, которая делает исключения мощными, заключается в том, что генерируемое исключение может вызывать любое количество слоев в стеке вызовов. Если вы просто вернете его, вы потеряете это свойство (но также и негативное влияние на производительность).

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

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

Ответ 6

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

Лучше было бы создать свою собственную структуру или класс, содержащий нужную вам информацию, и вместо этого вернуть ее.

public enum MyErrorType
{
    Abc,
    Xyz,
    Efg
};

public struct Result
{
    public bool Success;
    public MyErrorType ErrorType;
};

а затем метод:

private Result MyMethod()
{
   // code
}

Ответ 7

Я должен согласиться с тем, что в целом это действительно плохая идея.

Однако я могу подумать о сценарии или двух, где это может иметь смысл.

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

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

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

Но такие ситуации как исключение ** cough **, а не правило. Ни в коем случае я не хочу видеть исключение, переданное через границы классов библиотеки, и ни при каких обстоятельствах я не создавал бы исключение, не будучи уверенным, что оно будет запущено.

Ответ 8

Нет, это полностью уберет язык (технология, так как вы просто указали .NET).

Я также не согласен с тем, что это упрощает использование ваших API для других, кто-либо, кто научился и имеет базовое понимание программирования с использованием модели, которая включает исключения, будет сложнее использовать ваш код.

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

Ответ 9

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

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

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

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

Ответ 10

Никогда не видел метода, который возвращает исключение. Обычно метод возвращает значение, и этот метод может отбрасывать исключение. Но ive никогда не видел код с возвратом нового System.InvalidOperationException( "Logfile не может быть доступен только для чтения" ); например...

Итак, чтобы ответить на ваш вопрос, я бы сказал, да, это плохо...

Ответ 11

Нет, я бы не счел это хорошей практикой.

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

Вы не должны обрабатывать исключения в своих методах, если у вас действительно есть стратегия восстановления, которая стоит реализовать. Я не учитываю logging и re-throwing как стратегию восстановления.

Вы всегда можете избавить своих клиентов от необходимости писать блоки try/catch, бросая непроверенные исключения.

Ответ 12

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

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

Ответ 13

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

int Div(int x, int y)
{
return x/y;
}

Как бы вы реализовали правильную обработку ошибок для этого метода? Какое значение вы вернете на вызов в следующих случаях?

Div(10,0);
or(int.MinValue,-1); //result of int.MinValue == int.MaxValue + 1;

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

Обработка ошибок больше, чем try{] catch{}. Все, что входит в блок catch, должно принять разумное решение, основанное на ошибке. Просто поймать исключение и вернуть его - это не обработка ошибок; это сокрытие симптомов и сделает отладку в лучшем случае очень сложной и часто кошмарной.

Ответ 14

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

Ответ 15

Я часто возвращаю исключение с использованием этого шаблона, чтобы избежать нескольких блоков try {} catch, которые запутывают код, поэтому я бы сказал, что в некоторых случаях рекомендуется возвращать исключение.

public void Method(string text)
{
   //throw exception if needed
   throw new Exception("exception"); 
}

public bool TryMethod(string text, out Exception ex)
{
   try
   {
      Method("test")
      ex = null;
      return true;
   }
   catch(Exception e)
   {
      ex = e;
      return false;
   }
}

Ответ 16

Я согласен с тем, что возвращение вместо исключения исключений - это плохая практика по всем причинам, указанным выше, почти во всех случаях, кроме одного, где задание функций заключается в создании Исключений. Например, если вы используете библиотеку Com, которая возвращает результаты вместо повышения исключений, вы можете создать функцию, которая преобразует результаты в Exception:

static public ErrorException ReturnErrAsExcep()
{
    int errcd = 0;
    string errmsg = "";

    CrummyComLib.GetLastError(out errcd, out errmsg);
    ErrorException ex = new SAPErrorException(errmsg, errcd);
    return ex;
} 

Что я буду использовать как

int retval = CrummyComObject.DoStuff();

if (retval != 0)
    throw globals.ReturnErrAsExcep();