Повторный бросок решара

Рассмотрим этот метод (простите печальную попытку юмора Чака Норриса:)):

public class ChuckNorrisException : Exception
{
    public ChuckNorrisException()
    {
    }

    public ChuckNorrisException(string message)
        : base(message)
    {
    }

    public ChuckNorrisException(string message, Exception cause)
        : base(message, cause)
    {
    }

    protected ChuckNorrisException(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
    }
}

static void ExceptionTest(double x)
{
    try
    {
        double y = 10 / x;
        Console.WriteLine("quotient = " + y);
    }
    catch (Exception e)
    {
        e = e is DivideByZeroException ? new ChuckNorrisException("Only Chuck Norris can divide by 0!", e) :
            e;
        throw e;
    }
}

В resharper, я получаю предупреждение на строке "throw e", в которой говорится: "Вероятно, реконструирование исключений". Но, очевидно, в этом случае это не намерение, так как e может быть завернуто в ChuckNorrisException, и если я просто использую "throw", это исключение с оболочкой не будет выбрано.

Я знаю, что могу подавить предупреждение resharper, но тогда он будет отключен для всех сценариев, если я не ошибаюсь. Я просто задавался вопросом, столкнулся ли кто-нибудь еще с этим. Единственным обходным решением, которое я нашел, является создание другой переменной исключения (например, e2) и ее бросок. Это может быть лучшее, что я могу сделать здесь. Кажется, что resharper мог обнаружить эту проблему, хотя и быть достаточно умным, чтобы знать, что если e изменено, тогда throw e в порядке.

Спасибо.

[EDIT] Извините, я забыл шаг. Перед броском мне нужно регистрировать исключение, поэтому я не могу просто сделать:

e = e is DivideByZeroException ? new ChuckNorrisException("Only Chuck Norris can divide by 0!", e) :
            e;
throw e;

Мне нужно сделать:

e = e is DivideByZeroException ? new ChuckNorrisException("Only Chuck Norris can divide by 0!", e) :
            e;
LogException(e);
throw e;

Ответ 1

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

Здесь два случая:

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

  • Во-вторых, вы ловите и повторно выбросить исходное исключение. Если вы повторно бросите, вы никогда не следует использовать "throw e", поскольку он будет влиять на трассировку стека. Вот почему ReSharper печатает предупреждение. Чтобы повторно выбрасывать исключение, вы должны использовать ключевое слово "throw" самостоятельно.

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

В любом случае, описание почему вы получаете предупреждение. Вот что я сделал бы с этим:

catch(DivideByZeroException e)
{
    // we don't catch any other exceptions because we weren't
    // logging them or doing anything with the exception before re-throwing
    throw new ChuckNorrisException("Useful information", e);
}

* Edit - если вам нужно записывать исключения, вы можете просто сделать что-то подобное. Примечание. Это мое предпочтительное решение, так как я думаю, что он лучше читает и с меньшей вероятностью будет содержать ошибку, чем сами запросы на типы исключений:

// catch most specific exception type first
catch(DivideByZeroException e)
{
    Log(e); 
    throw new ChuckNorrisException("Useful information", e);
} 
catch(Exception e) // log & re-throw all other exceptions
{
    Log(e);
    throw; // notice this is *not* "throw e"; this preserves the stack trace
}

Другой вариант:

catch(Exception e)
{
    Log(e);
    if(e is DivideByZeroException)
    {
        // wrap + throw the exception with our new exception type
        throw new ChuckNorrisException("useful info", e);
    }

    // re-throw the original, preserving the stack trace
    throw;
}

Ответ 2

Это будет иметь тот же эффект, что и код, который вы опубликовали, и не должен вызывать предупреждение.

catch (DivideByZeroException de)
    {
        throw new ChuckNorrisException("Only Chuck Norris can divide by 0!", de);
    }
}