Как узнать, может ли какой-либо метод генерировать исключение

Я новичок в разработке для Windows 8 и С#, но у меня есть определенный опыт программирования на Java.

Итак, когда я пытаюсь сделать некоторый парсер Json (например) в java, я не могу сделать это без использования блока try-catch, и таким образом я могу обработать исключение, но когда я пытаюсь выполнить то же самое в С# (Windows 8), и я не использую блок try-catch, который он тоже работает, например:

if (json != null)
{
        JObject jObject = JObject.Parse(json);

        JArray jArrayUsers = (JArray)jObject["users"];

        foreach (JObject obj in jArrayUsers)
        {
            ListViewMainViewModel user = new ListViewMainViewModel(
                (String)obj["email"],
                (String)obj["token"],
                (String)obj["institution"],
                (String)obj["uuidInstitution"]);

            usersList.Add(user);
        }
        return usersList;
    }

}

Как я знаю, правильный способ - уловить JsonReaderException, но Visual Studio никогда не предупреждала меня об этом. Я хотел бы знать, есть ли простой способ узнать, генерирует ли какой-либо метод исключение, например, на java, используя eclipse (он обязательно реализует блок try-catch или код, который не компилируется)

Ответ 1

Вам нужно будет ознакомиться с документацией. В С# отсутствует ключевое слово throws.

Термин для того, что вы ищете, это проверенные исключения, и более подробную информацию можно найти в часто задаваемые вопросы по С#.

Ответ 2

В С# вы несете ответственность за обработку исключений - что ИМХО - лучший способ сделать это, чем реализация Java. По сути, исключение должно быть, а также исключительным, то есть: это не то, что вы всегда должны ожидать.

Рассмотрим этот странный (но общий) анти-шаблон:

try {


} catch (Exception ex) { /* Handler goes here */ }

Что именно это означает? Вы действительно будете обрабатывать каждое исключение, которое проходит здесь? Даже такие вещи, как OutOfMemoryException s? Это орехи. Единственное, что приведет этот шаблон, это подавление законных исключений, которые действительно должны привести к приложению - это довольно похоже на подход Java.

Подумайте о Exception как о знаке для программиста, который говорит: "Эй, среда просто вступила в невозможное состояние". Например, если я попытаюсь делить на ноль, и система выбрасывает DivideByZeroException, тогда система должна предупредить вас об этом, потому что это провал - тот, который система не может просто "отличить" от нее - и если вы просто подавите проблему, как это действительно помогает? В конце концов, это контрпродуктивно, поскольку все, что вы делаете, маскирует то, что на самом деле является невозможным состоянием приложения. Если вы делаете это много в своей заявке, тогда он в конечном итоге просто переходит в шлам токсичной неправильности. Тьфу!

Исключения также занимают много экранной недвижимости. Иногда мне хотелось бы, чтобы блоки try/catch/finally стали немного более упорядоченными, но потом я помню, что это побуждало бы людей использовать их больше, поэтому я быстро раскаиваюсь в этой позиции.

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

Ответ 3

С#, как уже упоминалось, не имеет проверенных исключений и благодарит добро.

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

  • Они навязывают свою волю потребляющему кодеру. Проверяемые исключения, по их определению, должны быть обработаны до того, как они будут выброшены из среды выполнения. Время выполнения фактически говорит кодеру "вы должны знать, что делать, когда я бросаю это, так что делайте это". Прежде всего, подумайте об этом; вам говорят ожидать чего-то, что происходит в исключительных случаях по самому его определению. Во-вторых, вы должны как-то справиться с этим. Хорошо, что все хорошо и хорошо, когда у вас есть возможность решить проблему, указывает исключение. К сожалению, у нас не всегда есть эта способность, и мы не всегда хотим делать все, что нужно. Если я пишу апплет простой формы, который выполняет преобразование данных, и я просто хочу, чтобы мое приложение умерло огненной смертью, если есть какие-то проблемы, я не могу просто ничего не поймать; Мне нужно подобрать все возможные стеки вызовов для каждого метода, которые могут что-то бросить и добавить то, что он может добавить в предложение throws (или быть крайне ленивым и поставить предложение "throws Exception" для каждого метода моей кодовой базы). Точно так же, если мое приложение сконструировано таким образом, что я не могу выбросить какое-либо конкретное исключение, возможно, потому, что я реализую интерфейс вне моего контроля, который не указывает его как потенциальный способный, тогда мои единственные варианты - полностью усвоить его и возвращать, возможно, недействительный результат для моих вызывающих абонентов, или обернуть исключение в неконтролируемый тип, подобный RuntimeException, и выбросить его таким образом (игнорируя весь проверенный механизм исключения, который не рекомендуется, конечно).
  • Они нарушают SOLID, особенно принцип Open-Closed. Внесите изменения, которые добавят проверенное исключение в ваш код, и если вы не можете обработать указанное исключение, все способы использования вашего метода должны либо обрабатывать его или отмечать себя как исключение. Использование, которое реконструировать должно быть обработано их собственными абонентами, или они должны быть отмечены как бросающие одно и то же исключение. Сделав изменение как хирургическое, как вызов альтернативного метода в определенной строке кода, теперь вам нужно проследить все возможные стеки вызовов и внести другие изменения в код, который работал просто отлично, просто чтобы сказать им, что ваш код может, возможно, исключение.
  • . По определению они создают абсорбирующие абстракции. Абонент, использующий метод с предложением "броски", должен, по сути, знать эти детали реализации о своей зависимости. Затем он должен, если он не желает или не может справиться с этими ошибками, информирует своих потребителей об этих ошибках. Проблема усугубляется, когда метод является частью реализации интерфейса; для того, чтобы объект мог его бросить, интерфейс должен указывать его как бросающийся, даже если не все реализации выкидывают это исключение.

    Java смягчает это, имея многоуровневую иерархию классов Exception; все исключения, связанные с I/O, являются (предположительно) IOExceptions, например, и интерфейсом с методами, которые связаны с IO-целями, могут указывать, что может быть выбрано исключение IOException, освобождая его от ответственности за указание каждого конкретного дочернего исключения IOException. Это вызывает почти столько же проблем, сколько оно решает; существуют десятки IOExceptions, которые могут иметь очень разные причины и разрешения. Таким образом, вы должны допросить каждое исключение IOException, которое вы ловите во время выполнения, чтобы получить его истинный тип (и вы мало или совсем не помогаете идентифицировать конкретные, которые могут быть выбраны), чтобы определить, может ли он обрабатывать автоматически, и как.

EDIT: Еще одна большая проблема:

  • Они предполагают, что try-catch - единственный способ справиться с возможной ситуацией исключения. Предположим, вы находитесь на С# в альтернативном юниверсе, где С# имеет проверенные исключения в стиле Java. Вы хотите, чтобы ваш метод открыл и прочитал файл с именем файла, переданным в него вызывающим. Как хороший маленький кодер, вы сначала подтверждаете, что файл существует в предложении guard, используя File.Exists(который никогда не будет генерировать исключение; для того, чтобы вернуть true, путь должен быть действительным, файл, указанный на пути, должен существовать, а исполняемая учетная запись пользователя должна иметь как минимум доступ для чтения к папке и файлу). Если File.Exists возвращает false, ваш метод просто не возвращает никаких данных, и ваши вызывающие абоненты знают, что делать (скажем, этот метод открывает файл, содержащий дополнительные данные конфигурации, а если он не существует, пуст или поврежден, ваша программа генерирует и использует конфигурацию по умолчанию).

    Если файл существует, вы вызываете File.Open. Ну, File.Open может выкинуть девять разных типов исключений. Но ни один из них, скорее всего, не произойдет, поскольку вы уже проверили использование File.Exists, что файл может быть открыт только для чтения пользователем, запускающим программу. Однако проверенный механизм исключений не волнует; метод, который вы используете, указывает, что он может выкинуть эти исключения, и поэтому вы должны либо обрабатывать их, либо указать, что ваш собственный метод может их бросить, даже если вы можете принять все меры предосторожности, чтобы предотвратить его. Ответ будет заключаться в том, чтобы усвоить их и вернуть null (или забыть оговорку охраны, а также просто уловить и обработать исключения File.Open), но в том, что шаблон, который вы пытались избежать с помощью предложения guard в первую очередь.

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

Что касается использования вместо этого в С#, лучше всего использовать комментарии к документации XML, чтобы сообщить непосредственному вызывающему абоненту, используя ваш метод, чтобы исключение могло быть потенциально отброшенного от него. XML-doc является эквивалентом .NET, эквивалентным комментариям JavaDoc, и используется почти таким же образом, но синтаксис отличается (три косая черта, сопровождаемая комментариями, окруженными системой тегов XML). Тег для исключения достаточно прост для указания. Чтобы эффективно документировать вашу кодовую базу, я рекомендую GhostDoc. Однако он будет генерировать комментарии к исключениям только для исключений, явно выведенных из документа, задокументированного, и вам придется заполнить некоторые пробелы.

Ответ 4

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

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

Подробнее об этом здесь: http://enterprisecraftsmanship.com/2015/03/20/functional-c-handling-failures-input-errors/