Пример “использования исключений к потоку управления”, [закрытому]

7
задан GurdeepS 15 July 2010 в 20:18
поделиться

9 ответов

Bad

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

int input1 = GetInput1();
int input2 = GetInput2();

try
{
    int result = input1 / input2;
    Output("{0} / {1} = {2}", input1, input2, result);
}
catch (OverflowException)
{
    Output("There was an overflow exception. Make sure input2 is not zero.");
}

Лучше

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

int input1 = GetInput1();
int input2 = GetInput2();

while (input2 == 0)
{
    Output("input2 must not be zero. Enter a new value.");
    input2 = GetInput2();
}

int result = input1 / input2;
Output("{0} / {1} = {2}", input1, input2, result);
10
ответ дан 6 December 2019 в 04:53
поделиться

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

try
{
    File.Open(@"c:\some nonexistent file.not here");
}
catch(FileNotFoundException)
{
    // do whatever logic is needed to create the file.
    ...
}
// proceed with the rest of your program.

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

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

14
ответ дан 6 December 2019 в 04:53
поделиться

Это примерно эквивалентно goto, за исключением худшего с точки зрения слова Exception и дополнительных накладных расходов. Вы говорите коду перейти к блоку catch:

bool worked;
try
{
    foreach (Item someItem in SomeItems)
    {
        if (someItem.SomeTestFailed()) throw new TestFailedException();
    }
    worked = true;
}
catch(TestFailedException testFailedEx)
{
    worked = false;
}
if (worked) // ... logic continues

Как видите, он выполняет некоторые (выдуманные) тесты; если они терпят неудачу, генерируется исключение, и сработало будет установлено значение false .

Гораздо проще, конечно, просто обновить bool, работало напрямую!

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

10
ответ дан 6 December 2019 в 04:53
поделиться

В настоящее время я работаю со сторонней программой, которая делает это. У них есть интерфейс «курсора» (в основном альтернатива IEnumerable), где единственный способ сообщить программе, что вы закончили, - это вызвать исключение. Код в основном выглядит так:

// Just showing the relevant section
bool finished = false;

public bool IsFinished()
{
    return finished;
}

// Using something like:
// int index = 0;
// int count = 42;

public void NextRecord()
{
    if (finished)
        return;

    if (index >= count)
        throw new APIProgramSpecificException("End of cursor", WEIRD_CONSTANT);
    else
        ++index;
}

// Other methods to retrieve the current value

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

2
ответ дан 6 December 2019 в 04:53
поделиться

Вот самый распространенный:

public bool TryParseEnum<T>(string value, out T result)
{
    result = default(T);

    try
    {
        result = (T)Enum.Parse(typeof(T), value, true);
        return true;
    }
    catch
    {
        return false;
    }
}
5
ответ дан 6 December 2019 в 04:53
поделиться

Я не люблю C #, но вы можете увидеть некоторое сходство между операторами try-catch-finally и обычными операторами потока управления if-then-else .

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

if (doSomething() == BAD) 
{
  //recover or whatever
}

, вы можете легко думать об этом в терминах try-catch:

try
{
  doSomething();
}
catch (Exception e)
{
  //recover or do whatever
}

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

Кроме того, вы можете смоделировать также оператор while без эффективного использования его условия:

while (!finished)
{
  //do whatever
}

может стать

try
{
  while (true)
  {
     doSomethingThatEventuallyWillThrowAnException();
  }
}
catch (Exception e)
{
  //loop finished
}
1
ответ дан 6 December 2019 в 04:53
поделиться

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

public void Search(Node node, object data)
{
    if(node.Data.Equals(data))
    {
        throw new ResultException(node);
    }
    else
    {
        Search(node.LeftChild, data);
        Search(node.RightChild, data);
    }    
}

Выполнение чего-то подобного - проблема по нескольким причинам.

  1. Это совершенно нелогично. Исключения предназначены для исключительных случаев. Что-то работающее по назначению (мы надеемся) никогда не должно быть исключительным сценарием.
  2. Вы не всегда можете полагаться на то, что исключение будет сгенерировано и передано вам. Например, если код, генерирующий исключение, выполняется в отдельном потоке, вам понадобится дополнительный код для его захвата.
  3. Это потенциальная проблема с производительностью. Существуют накладные расходы, связанные с исключениями, и если вы выбрасываете их много, вы можете увидеть падение производительности в своем приложении.

Здесь есть еще несколько примеров и интересное обсуждение .

Заявление об ограничении ответственности: приведенный выше код адаптирован из первого примера на этой вики-странице, чтобы преобразовать его в C #.

1
ответ дан 6 December 2019 в 04:53
поделиться

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

Для каждого файла в каталоге приложения он открывал файл и пытался разобрать его как XML. Если файл выдавал исключение (потому что это был не XML), он ловил исключение, подавлял его и пробовал следующий файл!

Когда партнер тестировал этот модуль, у него было всего 3 файла в каталоге приложения. Костяной поиск файлов конфигурации не оказал заметного влияния на запуск тестового приложения. Когда мы добавили его в наше приложение, в каталоге app было 100's файлов, и приложение застыло почти на минуту при запуске.

Чтобы добавить соли на рану, имя конфигурационного файла, который искал модуль, было предопределено и неизменно. Не было необходимости в каком-либо поиске файла.

Гениальность имеет свои пределы. Глупость безгранична.

1
ответ дан 6 December 2019 в 04:53
поделиться

Наверное, самое грубое нарушение, которое я когда-либо видел:

// I haz an array...
public int ArrayCount(object[] array)
{
    int count = 0;
    try
    {
        while (true)
        {
            var temp = array[count];
            count++;
        }
    }
    catch (IndexOutOfRangeException)
    {
        return count;
    }
}
2
ответ дан 6 December 2019 в 04:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: