Как повторно бросить InnerException, не теряя отслеживание стека в C#?

NullPointerException s - исключения, возникающие при попытке использовать ссылку, которая указывает на отсутствие местоположения в памяти (null), как если бы она ссылалась на объект. Вызов метода по нулевой ссылке или попытка получить доступ к полю нулевой ссылки вызовет функцию NullPointerException. Они наиболее распространены, но другие способы перечислены на странице NullPointerException javadoc.

Вероятно, самый быстрый пример кода, который я мог бы придумать для иллюстрации NullPointerException, be:

public class Example {

    public static void main(String[] args) {
        Object obj = null;
        obj.hashCode();
    }

}

В первой строке внутри main я явно устанавливаю ссылку Object obj равной null. Это означает, что у меня есть ссылка, но она не указывает на какой-либо объект. После этого я пытаюсь обработать ссылку так, как если бы она указывала на объект, вызывая метод на нем. Это приводит к NullPointerException, потому что нет кода для выполнения в местоположении, на которое указывает ссылка.

(Это техничность, но я думаю, что она упоминает: ссылка, которая указывает на null, равна 't то же, что и указатель C, указывающий на недопустимую ячейку памяти. Нулевой указатель буквально не указывает на в любом месте , который отличается от указаний на местоположение, которое оказывается недопустимым.)

290
задан shA.t 9 September 2018 в 01:23
поделиться

7 ответов

Я думаю, что Ваш лучший выбор состоял бы в том, чтобы просто поместить это в Ваш блок выгоды:

throw;

И затем извлекают innerexception позже.

32
ответ дан GEOCHET 23 November 2019 в 01:43
поделиться

Еще больше отражения...

catch (TargetInvocationException tiex)
{
    // Get the _remoteStackTraceString of the Exception class
    FieldInfo remoteStackTraceString = typeof(Exception)
        .GetField("_remoteStackTraceString",
            BindingFlags.Instance | BindingFlags.NonPublic); // MS.Net

    if (remoteStackTraceString == null)
        remoteStackTraceString = typeof(Exception)
        .GetField("remote_stack_trace",
            BindingFlags.Instance | BindingFlags.NonPublic); // Mono

    // Set the InnerException._remoteStackTraceString
    // to the current InnerException.StackTrace
    remoteStackTraceString.SetValue(tiex.InnerException,
        tiex.InnerException.StackTrace + Environment.NewLine);

    // Throw the new exception
    throw tiex.InnerException;
}

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

11
ответ дан abatishchev 23 November 2019 в 01:43
поделиться

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

10
ответ дан Siyual 23 November 2019 в 01:43
поделиться

Основываясь на ответе Пола Тернерса, я сделал метод расширения

    public static Exception Capture(this Exception ex)
    {
        ExceptionDispatchInfo.Capture(ex).Throw();
        return ex;
    }

, который никогда не достигался return ex, но преимущество в том, что я могу использовать throw ex.Capture() в качестве однострочного, так что компилятор не будет вызвать ошибку not all code paths return a value.

    public static object InvokeEx(this MethodInfo method, object obj, object[] parameters)
    {
        {
            return method.Invoke(obj, parameters);
        }
        catch (TargetInvocationException ex) when (ex.InnerException != null)
        {
            throw ex.InnerException.Capture();
        }
    }
1
ответ дан 23 November 2019 в 01:43
поделиться
public static class ExceptionHelper
{
    private static Action<Exception> _preserveInternalException;

    static ExceptionHelper()
    {
        MethodInfo preserveStackTrace = typeof( Exception ).GetMethod( "InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic );
        _preserveInternalException = (Action<Exception>)Delegate.CreateDelegate( typeof( Action<Exception> ), preserveStackTrace );            
    }

    public static void PreserveStackTrace( this Exception ex )
    {
        _preserveInternalException( ex );
    }
}

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

12
ответ дан 23 November 2019 в 01:43
поделиться

Ребята, вы круты... Скоро я стану некромантом.

    public void test1()
    {
        // Throw an exception for testing purposes
        throw new ArgumentException("test1");
    }

    void test2()
    {
            MethodInfo mi = typeof(Program).GetMethod("test1");
            ((Action)Delegate.CreateDelegate(typeof(Action), mi))();

    }
5
ответ дан 23 November 2019 в 01:43
поделиться

Это - это , возможно для сохранения трассировки стека перед измельчением без отражения:

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
}

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

// usage (A): cross-thread invoke, messaging, custom task schedulers etc.
catch (Exception e)
{
    PreserveStackTrace (e) ;

    // store exception to be re-thrown later,
    // possibly in a different thread
    operationResult.Exception = e ;
}

// usage (B): after calling MethodInfo.Invoke() and the like
catch (TargetInvocationException tiex)
{
    PreserveStackTrace (tiex.InnerException) ;

    // unwrap TargetInvocationException, so that typed catch clauses 
    // in library/3rd-party code can work correctly;
    // new stack trace is appended to existing one
    throw tiex.InnerException ;
}
85
ответ дан 23 November 2019 в 01:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: