C # повторно выбрасывает исключение за пределы области видимости - PullRequest
4 голосов
/ 01 февраля 2011

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

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

public Exception _error { get; set; }

public bool IsValid()
{
    try
    {
        //do something here to cause exception                

        return true;
    }
    catch (Exception ex)
    {
        _error = ex;
        return false;
    }
}

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

throw _error; //lose stack trace

throw new Exception("", _error) //lose type

Спасибо, что посмотрели или ответили.

EDIT:

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

После размышления над ответом и комментариями Питера, я теперь задаюсь вопросом, могло ли бы создание класса Исключения оболочки, подобного ниже, быть частичным решением. Это переопределяет как можно большую часть исключения, чтобы новое исключение выглядело как его неисключительное исключение, включая трассировку стека ... грязно, я знаю, но интересно: public class ExceptionWrapper : Exception { private Exception _innerException; public ExceptionWrapper(Exception ex) : base("", ex) { _innerException = ex; this.Source = ex.Source; this.HelpLink = ex.HelpLink; } public override string StackTrace { get { return _innerException.StackTrace; } } public override System.Collections.IDictionary Data { get { return _innerException.Data; } } public override string Message { get { return _innerException.Message; } } public new Exception InnerException { get { return _innerException.InnerException; } } }

Ответы [ 3 ]

7 голосов
/ 01 февраля 2011

Нет, это невозможно.

Однако вы обычно решаете эту проблему, заключая исключение в новое исключение:

throw new MyException("Wrapper", _error);

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

2 голосов
/ 01 февраля 2011

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

Например:

[Test]
public void test()
{
    Exception ex = new ArgumentNullException();

    Exception wrapped = (Exception)Activator.
        CreateInstance(ex.GetType(), "wrapped", ex);

    Type expectedType = typeof(ArgumentNullException);

    Assert.IsInstanceOf(expectedType, wrapped, "Is ArgumentNullException.");

    Assert.AreEqual(ex, wrapped.InnerException, "Exception is wrapped.");
}

Обновление

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

Я согласен, что подход очень " meh ", это скорее исследование идеи.Я бы не рекомендовал это.

Руководства по разработке исключений требуют конструктора по умолчанию, поэтому такое поведение может в любом случае продолжаться в фреймворке.Возможно, для какой-то неприглядной сериализации \ десериализации исключений через какую-то границу связи?

1 голос
/ 25 апреля 2017

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

public ExceptionDispatchInfo _error { get; private set; }

public bool IsValid()
{
    try
    {
        //do something here to cause exception                

        return true;
    }
    catch (Exception ex)
    {
        _error = ExceptionDispatchInfo.Capture(ex);
        return false;
    }
}

/// <summary>Throw underlying exception if invalid.</summary>
public void AssertWasValid() => _error?.Throw();

Теперь, это не сохраняет первоначального абонента. Отображаемая трассировка стека показывает вызовы из исходного блока try в код там, оператор, разбивающий оригинальную и новую части стека, а затем вызовы в саму ExceptionDispatchInfo.Throw() как новую часть показанного стека. Это похоже на то, как выглядят трассировки с кодом async. Если вам небезразличен оригинальный абонент, кажется, это не сработает. Но если вам нужно получить строку / метод, вызвавший исключение, этого должно быть достаточно.

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