Try/Catch — Как мне узнать, что ловить в нечетных/сложных случаях?

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

int x = 0;
try
{
    x = GetXFromSomeplaceThatCanFail();
}
catch //Possibly (Exception) or (Exception e)
{
    //Ignore The Failure Because We Don't Care If It Fails
}
if (x != 0) //Yes I know I can use finally blocks to do something similar, but this is just an example case
{
    //Do Things With x
}

Я полностью осознаю, что это «проглотит» такие вещи, как OutOfMemoryException, что является плохой практикой и может вызвать необнаруженные сбои/неявные ошибки, которые являются ужасными вещами.

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

Тем не менее, нет документации для проверки в нестандартных ситуациях, чтобы увидеть, какие исключения могут быть выброшены (или их трудно найти). Конкретный случай из моего собственного проекта (имена переменных стали универсальными, а код упрощен) использует динамический тип для захвата строкового поля только если оно существует или в противном случае корректно завершается с ошибкой, предоставляя в качестве результата "N/A" . Еще раз напомню, что я знаю, что это плохой код:

string theString = "Some Old Value From Previous Run/etc.";
try
{
    theString = (placeWhereValuesComeFrom as dynamic).TheString;
}
catch
{
    theString = "N/A";
}

В этом контексте placeWhereValuesComeFrom наследуется от BaseClass, который не предоставляет (и не должен) предоставлять TheString.

Я понимаю, что могу создать промежуточный класс, который предлагает TheString и наследуется от BaseClass, а затем наследуется от него. Тем не менее, динамическое решение было очень быстро введено в действие и хорошо работает. Если для моего конкретного сценария не будет предложено лучшее решение, я планирую добавить промежуточный класс и наследовать от него только соответствующие классы, а затем протестировать так:

theString = placeWhereValuesComeFrom is Subclass ? ((Subclass)placeWhereValuesComeFrom).TheString : "N/A";

Однако, если предположить, что я по какой-либо причине не хочу проводить рефакторинг для использования промежуточного класса, что мне здесь делать? Как я могу узнать, какие возможные исключения я должен безопасно игнорировать в блоке(ах) catch? Как насчет других подобных ситуаций, когда нет реального способа просто «поискать», какие исключения могут быть выброшены?


person Yushatak    schedule 15.08.2016    source источник
comment
Обычно, если вы можете обработать исключение, потому что знаете, как восстановиться после него, вы можете перехватить его (и выйти из него). Если вы не знаете, как восстановиться после исключения, вы должны просто позволить вашему приложению сбой и записать все детали этого сбоя.   -  person Callum Linington    schedule 15.08.2016
comment
Вы не узнаете, какие исключения следует игнорировать. Вместо этого вы перехватываете только те исключения, для обработки которых на самом деле пишете код. Если вы не знаете, что такое исключения, то по определению вы не можете их обрабатывать и не должны их перехватывать.   -  person Cody Gray    schedule 15.08.2016
comment
Я знаю, что если в приведенной динамике нет соответствующего поля, она вызовет исключение. Я хочу обработать это исключение. Однако я не знаю, что это за тип исключения, и документация для динамического типа НЕ ГОВОРИТ МНЕ. Есть и другие ситуации, когда это применимо — что в них делать?   -  person Yushatak    schedule 15.08.2016
comment
Вы либо находите лучшую документацию (в документации всегда должно быть указано, какие исключения, обрабатываемые вызывающей стороной, генерирует метод), либо вы тестируете ее, чтобы увидеть (что отстой, потому что она хрупкая и может сломаться при изменении библиотеки, но так любой другой подход, основанный на отражении).   -  person Cody Gray    schedule 15.08.2016
comment
Когда вы поймаете исключение, в (Exception ex) { ex.GetType().Name } вы получите наиболее производное имя класса для этого исключения!!   -  person Callum Linington    schedule 15.08.2016
comment
Или, если вы используете C#6, попробуйте фильтры исключений.   -  person ryanyuyu    schedule 15.08.2016
comment
@Yushatak, вы можете настроить сценарий, в котором вы знаете, что код не сработает, и буквально посмотрите на выброшенное исключение. Модульное тестирование также является отличным способом добиться этого. Но ответ на ваш общий вопрос, как узнать об X?, состоит в том, чтобы проверить рассматриваемую вещь!   -  person Chima Osuji    schedule 15.08.2016
comment
@ChimaOsuji Я знаю, что могу это протестировать, но когда в документации нет списка исключений, что, если я не подумаю об ошибке, и она возникнет в производстве? Я предпочитаю иметь возможность планировать и справляться с другими, которые могут возникнуть.   -  person Yushatak    schedule 15.08.2016
comment
@ryanyuyu Это очень круто, но я, к сожалению, застрял на C # 5 в своей рабочей среде (насколько я не могу это контролировать).   -  person Yushatak    schedule 15.08.2016
comment
@CodyGray Это официальная документация Microsoft по типу Microsoft, а это не так. Какая лучше документация в этом случае? Конечно, я мог читать источник, но мне не следует полагаться на внутреннее знание по тем же причинам хрупкости.   -  person Yushatak    schedule 15.08.2016
comment
Какое еще исключение вы ожидаете, кроме исключения привязки во время выполнения? Либо динамический объект имеет свойство TheString, либо нет. Единственное исключение, которое вам нужно обработать, — это сбой привязки, который всегда один и тот же; точный тип легко определить, просто запустив случай, который не удастся, и проверив тип исключения. Я не уверен, в чем здесь проблема.   -  person InBetween    schedule 15.08.2016
comment
@InBetween В документации мне не сказано, что RuntimeBindingException может быть выброшено, а это значит, что для выяснения этого мне нужно вручную проверить, что происходит, когда я передаю неправильный объект. Представьте, что у вас есть большая библиотека с аналогичными недокументированными исключениями — вы хотите чтобы проверить каждый режим отказа, который вы можете придумать? Я бы точно не стал. Надеялся на решение этой ситуации.   -  person Yushatak    schedule 15.08.2016


Ответы (1)


Единственное исключение, которое вы должны здесь обработать, — это сбой привязки во время выполнения; когда динамический объект не реализует TheString. Тип выбрасываемого исключения — Microsoft.System.CSharp.RuntimeBinder.RuntimeBinderException.

Итак, ваш код должен быть следующим:

try
{
    str = myDynamicObject.TheString;
}
catch (Microsoft.System.CSharp.RuntimeBinder.RuntimeBinderException)
{
    //Binding failure
    str = "N/A"
}
catch ( ... //exceptions you know TheString can throw, if any...)
{
    //Handle
}
// any other exception you don't know how To handle...don't handle it
person InBetween    schedule 15.08.2016
comment
Это допустимый код для решения описанного выше конкретного случая, но он не подходит для общей ситуации, когда документация ничего вам не говорит и вы не хотите вручную проверять сбои. - person Yushatak; 15.08.2016
comment
@Yushatak Тогда я просто думаю, что предпосылка вашего вопроса неверна. Вы должны ловить исключения, которые вы знаете, как обрабатывать, и ничего больше. Если вы не знаете, какие исключения вы можете получить, то вы определенно не знаете, как с ними обращаться. Либо вы используете плохо задокументированную библиотеку (используйте другую), либо вы используете библиотеку, которую вы не полностью понимаете (изучите ее и выполните модульное тестирование своего кода до смерти). - person InBetween; 15.08.2016
comment
Думаю, это самое близкое, что я получу к ответу. Если документация не говорит вам, вы должны либо исследовать сами, либо перейти к альтернативе. Я бы хотел, чтобы вы могли просто выделить код, а затем щелкнуть правой кнопкой мыши параметр «Список возможных исключений», который будет проходить через слой кода с помощью слой и перечислите исключения, которые могут быть сгенерированы (и если вы щелкнете по одному из них, он покажет вам, где он его нашел). В этот момент вы также можете поместить несколько кнопок для добавления обработчиков для выбранных или всех возможных исключений для сценариев, где вы можете правильно обрабатывать каждое из них по-своему. - person Yushatak; 15.08.2016