Использование необработанных исключений вместо Contains ()?

Представьте, что у объекта, с которым вы работаете, есть коллекция других объектов, связанных с ним, например, коллекция Controls в WinForm. Вы хотите проверить наличие определенного объекта в коллекции, но у коллекции нет Contains()метода. Есть несколько способов борьбы с этим.

  • Реализуйте свой собственный Contains()метод, просматривая все элементы в коллекции, чтобы увидеть, является ли один из них тем, что вы ищете. Похоже, это подход «наилучшей практики».
  • Недавно я наткнулся на некоторый код, где вместо цикла была попытка получить доступ к объекту внутри оператора try, как показано ниже:
try  
{  
    Object aObject = myCollection[myObject];  
}  
catch(Exception e)  
{  
    //if this is thrown, then the object doesn't exist in the collection
}

Мой вопрос: насколько плохой из практики программирования вы считаете второй вариант и почему? Как производительность по сравнению с циклом в коллекции?

11.08.2008 23:49:32
8 ОТВЕТОВ
РЕШЕНИЕ

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

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

Например, исключение может быть вызвано, потому что объект не существует в коллекции, или потому что сама коллекция является нулевой или потому что вы не можете привести myCollect [myObject] к aObject.

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

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

Мне особенно нравится эта цитата из второй статьи:

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

3
12.08.2008 01:13:20

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

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

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

0
12.08.2008 00:01:59

Последнее является приемлемым решением. Хотя я бы определенно поймал конкретное исключение (ElementNotFound?), Которое в этом случае выбрасывает коллекция.

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

-2
12.08.2008 00:03:04

Я должен был бы думать об этом больше о том, насколько мне это нравится ... мой инстинкт инстинктив, да, не так сильно ...

РЕДАКТИРОВАТЬ: комментарии Райана Фокса на исключительный случай идеально, я согласен

Что касается производительности, это зависит от индексатора на коллекции. C # позволяет переопределить оператор индексатора, поэтому, если он выполняет цикл for, такой как метод contains, который вы бы написали, то он будет таким же медленным (возможно, на несколько наносекунд медленнее из-за try / catch ... но ничего для беспокоиться о том, что если сам код не находится внутри огромного цикла).

Если индексатором является O (1) (или даже O (log (n)) ... или что-то более быстрое, чем O (n)), тогда решение try / catch будет быстрее, конечно.

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

0
12.08.2008 00:07:06

В общем, использование обработки исключений для потока программ и логики является плохой практикой. Я лично считаю, что в последнем варианте недопустимо использование исключений. Учитывая особенности языков, обычно используемых в наши дни (например, Linq и lambdas в C #), нет причин не писать свой собственный метод Contains ().

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

0
12.08.2008 00:10:19

Общее правило заключается в том, чтобы избегать использования исключений для потока управления, если только обстоятельства, которые вызовут исключение, не являются «исключительными» - например, чрезвычайно редкими!

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

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

4
12.08.2008 00:30:43

Исключения должны быть исключительными.

Нечто вроде «Коллекция отсутствует, потому что база данных выпала из-под нее», является исключительной

Что-то вроде «ключа нет» - это нормальное поведение для словаря.

Для вашего конкретного примера коллекции элементов управления winforms Controlsсвойство имеет ContainsKeyметод, который вы и должны использовать.

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

ПОЧЕМУ исключения должны быть исключительными, речь идет о двух вещах

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

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

0
12.08.2008 02:02:49

Посмотрите на это сообщение в блоге от Krzystof: http://blogs.msdn.com/kcwalina/archive/2008/07/17/ExceptionalError.aspx

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

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

0
17.08.2008 14:38:19