Возвращаемое значение .NET и брошенный исключительный вопрос о дизайне - PullRequest
4 голосов
/ 03 февраля 2011

Предположим, у нас есть метод обработки операций в древовидной иерархической структуре данных, расположенный в классе, обрабатывающем такую ​​структуру.

Давайте подробнее рассмотрим один из этих методов:

void MoveNode(Node currentNode, Node newParentNode)
{
    /* check if new parent isn't current's child already */
     if (newParentNode.LMargin < currentNode.LMargin && newParentNode.RMargin > currentNode.RMargin)
     {
        //DO WORK
     }
     else throw new ArgumentException("New parent node cannot be current's own child");
}

Состояния MSDN: не выбрасывайте исключения для управления потоком!

Мой вопрос : хорошо ли, по вашему мнению, использование ArgumentException или вы хотите использовать какое-то возвращаемое значение? Если да, то как бы вы предоставили сообщение об ошибке / проверке.

Ответы [ 4 ]

10 голосов
/ 03 февраля 2011

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

То, что вы не должны делать, это:

try
{
   MoveNode(...)
   //Do something
}
catch(ArgumentException e)
{
  //Do something else
}

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

Лично мне не нравится, что вы бросаете исключение в предложении else. Я предпочитаю выполнять проверку параметров в начале функции и сразу же генерировать исключение. Это предотвращает вложение кода без ошибок в несколько блоков if.

Существует три типа исключений

  1. Асинхронное исключение, такое как StackOverflow, OutOfMemory и ThreadAborted. Они могут случиться где угодно и не могут быть обработаны
  2. Ошибка исключений, таких как ArgumentException, зарегистрируйте их в обработчике верхнего уровня и исправьте ошибку
  3. Ожидаемые исключения, указывающие на ошибку, которая может быть обработана локально. Обычно вы используете их, когда ошибки редки, и вы не можете заранее знать, что операция вызовет ошибку. Ошибки ввода-вывода являются типичным примером этого.
    Причина обычно внешняя. Например, недоступный файл, сбой сети или неверные данные в файле, который вы пытаетесь проанализировать.

Эрик Липперт говорит об этих видах исключений в записи блога: Исключительные исключения

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

1 голос
/ 08 февраля 2011

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

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

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

1 голос
/ 03 февраля 2011

Если это неожиданная ошибка времени выполнения, как здесь, это исключение, в противном случае это должно быть возвращаемое значение. Смоделируйте свое решение для int.Parse (throws) / int.TryParse (возвращаемое значение), первое - для обстоятельств, когда вы знаете, что вещи должны быть int (например, для синтаксического анализа типизированной структуры), другое - для проверки ввода пользователя (ошибки ввода ожидаются от пользователя).

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

1 голос
/ 03 февраля 2011

Я не думаю, что это случай "не использовать исключение для управления потоком".

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

...