Как документировать выброшенные исключения в c # /. Net

141

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

Я хочу предоставить хорошую информацию Intellisense, но не уверен, как документировать возникшие исключения.

В следующем примере:

public void MyMethod1()
{
    MyMethod2();

    // also may throw InvalidOperationException
}

public void MyMethod2()
{
    System.IO.File.Open(somepath...); // this may throw FileNotFoundException

    // also may throw DivideByZeroException
}

Я знаю, что разметка для документирования исключений:

/// <exception cref="SomeException">when things go wrong.</exception>

Я не понимаю, как документировать исключения, создаваемые кодом, вызываемым MyMethod1() ?

  • Должен ли я документировать исключения, созданные MyMethod2()
  • Должен ли я документировать возникшие исключения File.Open()?

Как лучше всего задокументировать возможные исключения?

Арнольд Зокас
источник
4
Я знаю, что это не совсем то, о чем вы спрашивали (и это действительно старый вопрос), но Эрик Липперт (главный разработчик компилятора Microsoft C # и команды разработчиков) написал в блоге сообщение о 4 типах исключений, которые, как я думаю, каждый разработчик следует подумать при написании кода обработки исключений: blogs.msdn.com/b/ericlippert/archive/2008/09/10/…
javajavajavajavajava
@javajavajavajavajava Спасибо за ссылку - обязательно стоит прочитать.
Арнольд Зокас
1
Я думаю, что это правильный вопрос, потому что совсем не очевидно, как правильно документировать исключения в C #, а просмотры 50K показывают, что это также не очевидно для многих людей. Второй по количеству голосов ответ очень полезен, так как показывает использование существующих xmldocs для документирования этого. Голосование для открытия. Эта "основанная на мнении" причина убивает множество действительно очень полезных вопросов программирования.
Алексей

Ответы:

111

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

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

Еще одно место, где вы можете захотеть сделать это таким образом, - это если ваш метод находится в вашем API. Так же, как фасад упрощает несколько интерфейсов в один интерфейс, ваш API должен упростить несколько исключений в одно исключение. Упрощает использование вашего кода для звонящих.


Чтобы ответить на некоторые из опасений Эндрю (из комментариев), есть три типа исключений: те, о которых вы не знаете, те, о которых вы знаете и с которыми ничего не можете сделать, и те, о которых вы знаете и с которыми можете что-то сделать.

Те, о которых вы не знаете, хотите отпустить. Это принцип быстрого сбоя - лучше ваше приложение выйдет из строя, чем войдет в состояние, когда вы можете в конечном итоге испортить свои данные. Сбой расскажет вам о том, что произошло и почему, что может помочь вывести это исключение из списка «тех, о которых вы не знаете».

Те, о которых вы знаете и с которыми ничего не можете поделать, - это исключения, такие как OutOfMemoryExceptions. В крайних случаях вы можете захотеть обрабатывать подобные исключения, но если у вас нет каких-то довольно выдающихся требований, вы относитесь к ним как к первой категории - отпустите. Вы должны задокументировать эти исключения? Вы бы выглядели довольно глупо, документируя OOM для каждого отдельного метода, который обновляет объект.

Те, о которых вы знаете и можете что-то сделать, вы должны документировать и упаковывать.

Вы можете найти еще несколько рекомендаций по обработке исключений здесь.


источник
3
Должен признать, это звучит не очень практично. Я не могу себе представить, сколько потенциальных исключений может быть вызвано любым кодом, который я могу вызвать, плюс есть такие вещи, как OutOfMemoryException, которые вы не хотели бы перехватывать и переносить.
Эндрю Хэйр,
3
Будет ли ваш ответ хорош, но на самом деле это два ответа, которые противоречат друг другу. «документируйте каждое исключение, которое может быть вызвано вашим кодом» и «Те, о которых вы знаете и можете что-то сделать, вы должны документировать».
tymtam
2
@Tymek: Нет. Первая половина ответила на вопрос «как мне документировать исключения», вторая часть указала на очевидно очевидный ответ на вопрос «какие исключения я должен документировать». Первое не означает, что вы документируете каждое исключение, которое может когда-либо произойти. Некоторые люди слишком буквальны, для чего нужна вторая половина.
5
@Tymek Я думаю, ваша точка зрения может заключаться в том, что если вы можете что-то с этим сделать, почему бы не сделать что-то с этим вместо того, чтобы заново выбросить и задокументировать это? Было бы более правдиво сказать: «Те, о которых вы знаете, что клиентский код может что-то сделать». Это снимает противоречие, потому что это идеальные исключения для документирования.
мес.
Что касается исключений, которые вы «отпускаете», вы всегда можете поймать их на каком-то более низком уровне, который их регистрирует или что-то в этом роде. Ты знаешь; просто создать удобный способ предотвращения сбоя программы.
Nyerguds
96

Вам следует использовать стандартную xml-документацию .

/// <exception cref="InvalidOperationException">Why it's thrown.</exception>
/// <exception cref="FileNotFoundException">Why it's thrown.</exception>
/// <exception cref="DivideByZeroException">Why it's thrown.</exception>
public void MyMethod1()
{
    MyMethod2();
    // ... other stuff here
}

/// <exception cref="FileNotFoundException">Why it's thrown.</exception>
/// <exception cref="DivideByZeroException">Why it's thrown.</exception>
public void MyMethod2()
{
    System.IO.File.Open(somepath...);
}

/// <exception cref="FileNotFoundException">Why it's thrown.</exception>
public void MyMethod3()
{
    try
    {
        MyMethod2();
    }
    catch (DivideByZeroException ex)
    {
        Trace.Warning("We tried to divide by zero, but we can continue.");
    }
}

Ценность такого подхода в том, что вы предоставляете документацию об известных исключениях, которые могут произойти. Эта документация доступна в intellisense, если вы используете Visual Studio, и может позже напомнить вам (или другим) об ожидаемых вами исключениях.

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

озноб42
источник
1
Как это добавляет ценности? Например, все эти исключения являются производными типа Exception. По моему опыту, нецелесообразно думать обо всех остальных типах исключений, которые могут быть выброшены из других API, вызываемых в ваших методах. Я хочу сказать, что мы не должны беспокоиться о каких-либо исключениях, которые генерируются из метода, кроме тех, которые несут какую-либо бизнес-информацию.
Illuminati
35

Вы можете упростить процесс документирования, используя несколько отличных надстроек. Одна из них - GhostDoc , бесплатная надстройка для Visual Studio, которая генерирует комментарии в XML-документах. Кроме того, если вы используете ReSharper , обратите внимание на отличный плагин Agent Johnson для ReSharper, который добавляет возможность генерировать XML-комментарии для выброшенных исключений.

Обновление: похоже, что Agen Johnson недоступен для R # 8, в качестве альтернативы проверьте Exceptional для ReSharper ...

Шаг 1. GhostDoc генерирует XML-комментарий (Ctrl-Shift-D), а плагин Agent Johnson для ReSharper также предлагает задокументировать исключение:

шаг 1

Шаг 2: Используйте горячую клавишу ReSharper (Alt-Enter), чтобы также добавить документацию по исключению:

шаг 2 http://i41.tinypic.com/osdhm

Надеюсь, это поможет :)

Игал Табачник
источник
Ссылки на tinypic не работают.
Аневс считает SE злом
12

Насколько я понимаю, цель использования элемента <exception> состоит в том, чтобы использовать его при украшении методов, а не в исключениях:

/// <summary>Does something!</summary>
/// <exception cref="DidNothingException">Thrown if nothing is actually done.</exception>
public void DoSomething()
{
// There be logic here
}

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

Если говорить более конкретно, возможно, вы сможете поймать и выбросить свои собственные настраиваемые исключения?

Даниэль Шаффер
источник
4

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

public void MyMethod2()
{
    System.IO.File.Open(somepath...); // this may throw FileNotFoundException
}

становится

/// <exception cref="FileNotFoundException">Thrown when somepath isn't a real file.</exception>
public void MyMethod2()
{
    FileInfo fi = new FileInfo( somepath );
    if( !fi.Exists )
    {
        throw new FileNotFoundException("somepath doesn't exists")
    }
    // Maybe go on to check you have permissions to read from it.

    System.IO.File.Open(somepath...); // this may still throw FileNotFoundException though
}

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

Роуленд Шоу
источник
1
Не уверен, в чем смысл этой проверки, если вы собираетесь просто продублировать исключение, которое Openвызовет в любом случае (не говоря уже о том, что, как вы заметили, есть гонка, и проверка не гарантирует успеха Open). .
Matt Энрайт
1
@MattEnright Конечно, но я сделал это немного надуманным, чтобы проиллюстрировать суть ...
Роуленд Шоу
1

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

Чтобы скрыть детали реализации, я бы попытался самостоятельно обработать некоторые исключения из MyMethod2.

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

GvS
источник
1

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

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

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

http://www.josefcobonnin.com/post/2009/01/11/Xml-Documentation-Comments-Exceptions-I.aspx http://www.josefcobonnin.com/post/2009/01/15/Xml-Documentation -Комментарии-Исключения-II.aspx

С уважением.


источник
0

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

Помните, что это нужно для того, чтобы сообщить вызывающему абоненту, чего ожидать, чтобы он мог выбрать, что с этим делать.

Дэмиен
источник