Вопросы производительности для создания исключений - PullRequest
7 голосов
/ 10 августа 2008

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

try
{
    ... // some code
}
catch (Exception ex)
{
    ... // Do something
    throw new CustomException(ex);
}

По сути, кодер делает то, что он включает исключение в пользовательское исключение и выдает его снова.

Как это отличается по производительности от следующих двух:

try
{
    ... // some code
}
catch (Exception ex)
{
    .. // Do something
    throw ex;
}

или

try
{
    ... // some code
}
catch (Exception ex)
{
    .. // Do something
    throw;
}

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

Ответы [ 8 ]

10 голосов
/ 10 августа 2008

@ Брэд Туттероу

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

Я также второй комментарий Брэда, что вы не должны беспокоиться о производительности. Этот вид микрооптимизации - УЖАСНАЯ идея. Если вы не говорите о создании исключения в каждой итерации цикла for, который выполняется в течение длительного времени, вы, скорее всего, не столкнетесь с проблемами производительности из-за использования вашего исключения.

Всегда оптимизируйте производительность, когда у вас есть метрики, которые указывают на то, что вам НЕОБХОДИМО оптимизировать производительность, а затем попадайте в точки, которые оказались виновником.

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

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

2 голосов
/ 16 августа 2008

Не делай:

try
{
    // some code
}
catch (Exception ex) { throw ex; }

Поскольку это потеряет трассировку стека.

Вместо этого:

try
{
    // some code
}
catch (Exception ex) { throw; }

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

2 голосов
/ 10 августа 2008

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

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

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

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

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

В качестве примечания:

Мне лично не нравится использование исключений (иерархий классов, производных от класса Exception) для реализации логики. Как и в случае:

try {
        // something that will raise an exception almost half the time
} catch( InsufficientFunds e) {
        // Inform the customer is broke
} catch( UnknownAccount e ) {
        // Ask for a new account number
}
2 голосов
/ 10 августа 2008

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

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

Редактировать: Удалено что-то, что было просто неправильно, и Майк был достаточно любезен, чтобы указать.

1 голос
/ 10 августа 2008

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

Я лично использую пользовательские исключения, если хочу отделить определенные зависимости в коде. Например, у меня есть метод, который загружает данные из файла XML. Это может пойти не так по-разному.

Возможно, не удалось прочитать с диска (FileIOException), пользователь может попытаться получить к нему доступ откуда-то, где ему запрещено (SecurityException), файл может быть поврежден (XmlParseException), данные могут быть в неправильном формате ( DeserialisationException).

В этом случае, чтобы вызывающему классу было легче разобраться во всем этом, все эти исключения перебрасывают одно пользовательское исключение (FileOperationException), так что вызывающей стороне не нужны ссылки на System.IO или System.Xml, но все еще может получить доступ к той ошибке, которая произошла через перечисление и любую важную информацию.

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

public bool Load(string filepath)
{
  if (File.Exists(filepath)) //Avoid throwing by checking state
  {
    //Wrap anyways in case something changes between check and operation
    try { .... }
    catch (IOException ioFault) { .... }
    catch (OtherException otherFault) { .... }
    return true; //Inform caller of success
  }
  else { return false; } //Inform caller of failure due to state
}
0 голосов
/ 30 сентября 2008

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

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

0 голосов
/ 10 августа 2008

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

Сказав, что эти три блока кода имеют очень различное (внешнее) поведение, поэтому сравнивать их - все равно что задавать вопрос, эффективнее ли QuickSort, чем Добавление элемента в красно-черное дерево. Это не так важно, как правильный выбор.

0 голосов
/ 10 августа 2008

Бросок в вашем первом примере накладывается на создание нового объекта CustomException.

Повторный выброс в вашем втором примере вызовет исключение типа Exception.

Повторный выброс в вашем третьем примере вызовет исключение того же типа, который был выдан вашим "некоторым кодом".

Таким образом, во втором и третьем примерах используется меньше ресурсов.

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