Как исключить исключение без сброса трассировки стека?

16

Это следующий вопрос: Есть ли разница между «throw» и «throw ex» ?

Есть ли способ извлечь новый метод обработки ошибок без сброса трассировки стека?

[EDIT] Я буду пытаться как «внутренний метод», так и другой ответ , предоставленные Earwicker, и посмотрите, какой из них лучше подойдет, чтобы отметить ответ.

    
задан Sung 08.04.2009 в 16:32
источник
  • Что значит «извлечь новый метод обработки ошибок»? –  Marc Gravell♦ 08.04.2009 в 16:33
  • @marc да точно –  Sung 08.04.2009 в 16:45
  • Меня больше интересует, как эмулировать «бросок»; в извлеченном методе обработки ошибок. –  Sung 08.04.2009 в 16:48
  • Вы не можете сделать «бросок»; внутри самого извлеченного метода, так как этот метод также может быть вызван без выхода из блока catch (предположим, например, вызвать его с помощью «new Exception ()» в качестве аргумента). Единственный способ добиться этого - это, вероятно, тот, который предлагается в моем ответе с кодом возврата. –  Lucero 08.04.2009 в 17:08

5 ответов

25

Не уверен, если вы имеете в виду это, но мое предложение в вашем другом вопросе касалось этого.

Если ваш обработчик возвращает логическое значение, было ли обработано исключение или нет, вы можете использовать это в своем предложении catch:

catch (Exception ex) {
  if (!HandleException(ex)) {
    throw;
  }
}
    
ответ дан Lucero 08.04.2009 в 16:36
  • + помечено как ответ: кажется, что проще всего отследить ошибку до источника и легко читать. Кстати, мой метод «HandleException» называется «TryHandleRecoverableException». –  Sung 08.04.2009 в 17:25
41

Да; Это свойство InnerException для.

catch(Exception ex)
{
    throw new YourExceptionClass("message", ex);
}

Это позволит вам добавить свою собственную логику, а затем бросить свой собственный класс исключений. StackTrace из экземпляра YourExceptionClass будет изнутри этого кодового блока, но исключение InnerException будет исключением, которое вы поймали, с ранее созданным StackTrace.

    
ответ дан Adam Robinson 08.04.2009 в 16:34
  • Недостатком этого подхода на практике является то, что многие журналы ошибок содержат только детали самого верхнего исключения. Такой подход позволяет избежать этого. –  Drew Noakes 07.01.2017 в 23:32
34

В .NET Framework 4.5 теперь есть ExceptionDispatchInfo , который поддерживает этот точный сценарий. Он позволяет фиксировать полное исключение и перетаскивать его из другого места, не переписывая содержащуюся трассировку стека.

пример кода из-за запроса в комментарии

using System.Runtime.ExceptionServices;

class Test
{
    private ExceptionDispatchInfo _exInfo;

    public void DeleteNoThrow(string path)
    {
        try { File.Delete(path); }
        catch(IOException ex)
        {
            // Capture exception (including stack trace) for later rethrow.
            _exInfo = ExceptionDispatchInfo.Capture(ex);
        }
    }

    public Exception GetFailure()
    {
        // You can access the captured exception without rethrowing.
        return _exInfo != null ? _exInfo.SourceException : null;
    }

    public void ThrowIfFailed()
    {
        // This will rethrow the exception including the stack trace of the
        // original DeleteNoThrow call.
        _exInfo.Throw();

        // Contrast with 'throw GetFailure()' which rethrows the exception but
        // overwrites the stack trace to the current caller of ThrowIfFailed.
    }
}
    
ответ дан Zarat 27.09.2012 в 12:16
  • Интересно, но было бы неплохо увидеть пример кода –  Konstantin Spirin 08.01.2013 в 04:19
  • @KonstantinSpirin: Думал, что доступные методы и документы MSDN были довольно очевидны, но в любом случае добавили некоторый образец кода, чтобы дать обзор без необходимости переходить на MSDN. –  Zarat 08.01.2013 в 05:41
  • Это именно то, что я хочу, но, к сожалению, я не на 4.5 для моего текущего проекта. Но +1 за то, что я понял эту приятную новую функцию. –  AnorZaken 13.10.2015 в 04:37
5

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

Однако вы можете поместить исходное исключение в свое новое исключение как «InnerException». Будет ли это делать то, что вы ищете?

    
ответ дан Brian Genisio 08.04.2009 в 16:35
  • Я хочу подражать «броску»; в извлеченном методе обработки ошибок. –  Sung 08.04.2009 в 16:52
2

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

Если вы хотите быть очень осторожным в этом, это не очень хорошая идея. Лучше никогда не поймать исключение в первую очередь. Причина в том, что данный обработчик try/catch не должен принимать решение о запуске вложенных блоков finally для исключений, которые он не ожидает увидеть. Например, если есть NullReferenceException , вероятно, очень плохая идея продолжить выполнение какого-либо кода, так как это, вероятно, вызовет другое подобное исключение. И finally блоки - это просто код, как и любой другой код. Как только исключение будет поймано в первый раз, будут блокированы любые finally блоков в стеке под try/catch , к тому времени уже слишком поздно - может быть создано другое исключение, а это означает, что исходное исключение потеряны.

Это означает (в C #), что вы должны тщательно написать отдельный обработчик catch для всех типов исключений, которые вы хотите поймать. Это также означает, что вы можете фильтровать только по типу исключений. Это иногда очень тяжелый совет.

Должно быть возможно отфильтровывать исключения другими способами, но в C # это не так. Тем не менее, это возможно в VB.NET, и сам BCL использует это, имея небольшой код, написанный на VB.NET, поэтому он может фильтровать исключения более удобным способом.

Вот подробное объяснение с образцом кода VB.NET из блога команды CLR.

И вот мои два цента.

    
ответ дан Daniel Earwicker 08.04.2009 в 16:50
  • «Захватываете ли вы исключения, которые хотите затем фильтровать более тщательно, чтобы потом вы могли передумать, решите не обращаться с ними и не переверните их?» -> Да. –  Sung 08.04.2009 в 16:56
  • «наконец» код всегда выполняется, независимо от того, произошло ли исключение и как вы обрабатываете исключение. Я не вижу отношения к вопросу ... –  Lucero 08.04.2009 в 17:05
  • +1 для ссылки .. полезная вещь, которую имеет VB, и C # не ... странно. Возможно, это потому, что разработчики C # хотят, чтобы вы правильно изучали определенные исключения и проектировали иерархии исключений, чтобы вам не приходилось ловить и реконструировать несколько конкретных выводов –  Gishu 08.04.2009 в 17:29
  • +1 Earwicker, ваши 2центы были действительно замечательными. Я хотел бы использовать класс «Исключения» где-то в коде. –  Sung 08.04.2009 в 17:32
  • @ Lucero - не обязательно. Попробуйте обработать AppDomain.UnhandledException, и вы обнаружите, что он срабатывает до того, как, наконец, будут запущены блоки, вы получите возможность вызвать Environment.FailFast, прежде чем они это сделают. Но любой промежуточный улов / бросок будет мешать этой возможности. –  Daniel Earwicker 08.04.2009 в 17:48
Показать остальные комментарии