Использование необработанных исключений вместо Contains ()? - PullRequest
6 голосов
/ 12 августа 2008

Представьте, что у объекта, с которым вы работаете, есть коллекция других объектов, связанных с ним, например, коллекция 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
}

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

Ответы [ 8 ]

4 голосов
/ 12 августа 2008

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

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

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

2 голосов
/ 12 августа 2008

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

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

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

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

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

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

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

0 голосов
/ 17 августа 2008

Взгляните на этот пост в блоге от Krzystof: http://blogs.msdn.com/kcwalina/archive/2008/07/17/ExceptionalError.aspx

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

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

0 голосов
/ 12 августа 2008

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

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

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

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

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

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

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

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

0 голосов
/ 12 августа 2008

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

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

0 голосов
/ 12 августа 2008

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

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

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

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

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

0 голосов
/ 12 августа 2008

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

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

0 голосов
/ 12 августа 2008

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...