Сведения о вопросе

MAT

12:46, 16th August, 2020

Теги

Рекомендации по отлову и повторному выбрасыванию исключений .NET

Просмотров: 430   Ответов: 11

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

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

Против:

try
{
    //some code
}
catch
{
    throw;
}



  Сведения об ответе

lool

04:59, 16th August, 2020

Способ сохранения стека trace заключается в использовании throw; , это также допустимо

try {
  // something that bombs here
} catch (Exception ex)
{
    throw;
}

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

Майк также прав, предполагая, что исключение позволяет вам передать исключение (что рекомендуется).

У Карла Сегина также есть отличная запись об обработке исключений в его электронной книге "Основы программирования ", которая является отличным чтением.

Правка: рабочая ссылка на основы программирования pdf. Просто найдите в тексте "exception".


  Сведения об ответе

ЯЯ__4

05:22, 14th August, 2020

Если вы создадите новое исключение с начальным исключением, вы также сохраните начальный стек trace..

try{
} 
catch(Exception ex){
     throw new MoreDescriptiveException("here is what was happening", ex);
}


  Сведения об ответе

piter

11:52, 10th August, 2020

На самом деле, есть некоторые ситуации, когда заявление throw не сохранит информацию StackTrace. Например, в приведенном ниже коде:

try
{
  int i = 0;
  int j = 12 / i; // Line 47
  int k = j + 1;
}
catch
{
  // do something
  // ...
  throw; // Line 54
}

StackTrace будет означать, что строка 54 вызвала исключение, хотя оно было вызвано в строке 47.

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
   at Program.WithThrowIncomplete() in Program.cs:line 54
   at Program.Main(String[] args) in Program.cs:line 106

В ситуациях, подобных описанной выше, есть два варианта предварительной настройки исходного StackTrace:

Вызов Exception.InternalPreserveStackTrace

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

private static void PreserveStackTrace(Exception exception)
{
  MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
    BindingFlags.Instance | BindingFlags.NonPublic);
  preserveStackTrace.Invoke(exception, null);
}

У меня есть недостаток в том, что я полагаюсь на частный метод сохранения информации StackTrace. Он может быть изменен в будущих версиях фреймворка .NET. Приведенный выше пример кода и предложенное ниже Решение были извлечены из веб- блога Fabrice MARGUERIE .

Вызов Exception.SetObjectData

Техника ниже предложил Антон Тихого , как ответ в C#, как я могу повторно InnerException без потери стека вопрос trace .

static void PreserveStackTrace (Exception e) 
{ 
  var ctx = new StreamingContext  (StreamingContextStates.CrossAppDomain) ; 
  var mgr = new ObjectManager     (null, ctx) ; 
  var si  = new SerializationInfo (e.GetType (), new FormatterConverter ()) ; 

  e.GetObjectData    (si, ctx)  ; 
  mgr.RegisterObject (e, 1, si) ; // prepare for SetObjectData 
  mgr.DoFixups       ()         ; // ObjectManager calls SetObjectData 

  // voila, e is unmodified save for _remoteStackTraceString 
} 

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

protected Exception(
    SerializationInfo info,
    StreamingContext context
)

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


  Сведения об ответе

lats

01:03, 8th August, 2020

Когда вы throw ex, вы, по сути, создаете новое исключение и упускаете исходную информацию стека trace. throw является предпочтительным методом.


  Сведения об ответе

fo_I_K

01:00, 5th August, 2020

Эмпирическое правило состоит в том, чтобы не ловить и не бросать основной объект Exception . Это заставляет вас быть немного умнее в отношении исключений; другими словами, вы должны иметь явный catch для SqlException , чтобы ваш код обработки не делал что-то неправильно с NullReferenceException .

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


  Сведения об ответе

VERSUION

12:51, 11th August, 2020

Никто не объяснил разницу между ExceptionDispatchInfo.Capture( ex ).Throw() и простым throw, так что вот она. Тем не менее, некоторые люди заметили, что проблема с throw .

Полный способ перестроить перехваченное исключение - использовать ExceptionDispatchInfo.Capture( ex ).Throw() (доступно только из .Net 4.5).

Ниже приведены случаи, необходимые для проверки этого:

1.

void CallingMethod()
{
    //try
    {
        throw new Exception( "TEST" );
    }
    //catch
    {
    //    throw;
    }
}

2.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch( Exception ex )
    {
        ExceptionDispatchInfo.Capture( ex ).Throw();
        throw; // So the compiler doesn't complain about methods which don't either return or throw.
    }
}

3.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch
    {
        throw;
    }
}

4.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch( Exception ex )
    {
        throw new Exception( "RETHROW", ex );
    }
}

Случай 1 и Случай 2 дадут вам стек trace, где номер строки исходного кода для метода CallingMethod является номером строки throw new Exception( "TEST" ) .

Однако в случае 3 вы получите стек trace, где номер строки исходного кода для метода CallingMethod является номером строки вызова throw . Это означает, что если строка throw new Exception( "TEST" ) окружена другими операциями, вы понятия не имеете, в каком номере строки на самом деле возникло исключение.

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


  Сведения об ответе

qwerty101

07:34, 13th August, 2020

Вы всегда должны использовать "throw;", чтобы перестроить исключения в .NET,

Обратитесь к этому, http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx

В основном MSIL (CIL) имеет две инструкции-"throw" и "rethrow":

  • C#'s "throw ex;" компилируется в MSIL "throw"
  • C#'s "throw;"-в MSIL "rethrow"!

В принципе, я вижу причину, по которой "throw ex" переопределяет стек trace.


  Сведения об ответе

#hash

00:18, 22nd August, 2020

Несколько человек на самом деле упустили очень важный момент - 'throw' и 'throw ex' могут делать то же самое, но они не дают вам решающую часть imformation, которая является линией, где произошло исключение.

Рассмотрим следующий код:

static void Main(string[] args)
{
    try
    {
        TestMe();
    }
    catch (Exception ex)
    {
        string ss = ex.ToString();
    }
}

static void TestMe()
{
    try
    {
        //here's some code that will generate an exception - line #17
    }
    catch (Exception ex)
    {
        //throw new ApplicationException(ex.ToString());
        throw ex; // line# 22
    }
}

Когда вы делаете либо 'throw', либо 'throw ex', вы получаете стек trace, но line# будет #22, поэтому вы не можете понять, какая именно строка вызвала исключение (если только у вас нет только 1 или нескольких строк кода в блоке try). Чтобы получить ожидаемую строку #17 в вашем исключении, вам придется создать новое исключение с исходным стеком исключений trace.


  Сведения об ответе

прога

23:28, 7th August, 2020

Я бы определенно использовал:

try
{
    //some code
}
catch
{
    //you should totally do something here, but feel free to rethrow
    //if you need to send the exception up the stack.
    throw;
}

Это сохранит ваш стек.


  Сведения об ответе

baggs

11:42, 16th August, 2020

Вы также можете использовать:

try
{
// Dangerous code
}
finally
{
// clean up, or do nothing
}

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


  Сведения об ответе

ЯЯ__4

02:52, 15th August, 2020

К вашему сведению, я только что проверил это, и стек trace, о котором сообщает ' throw;', не является полностью правильным стеком trace. Пример:

    private void foo()
    {
        try
        {
            bar(3);
            bar(2);
            bar(1);
            bar(0);
        }
        catch(DivideByZeroException)
        {
            //log message and rethrow...
            throw;
        }
    }

    private void bar(int b)
    {
        int a = 1;
        int c = a/b;  // Generate divide by zero exception.
    }

Стек trace правильно указывает на источник исключения (сообщенный номер строки), но номер строки, сообщенный для foo(), является строкой оператора throw;, следовательно, вы не можете сказать, какой из вызовов bar() вызвал исключение.


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться