Должен ли я проверять, присутствует ли конкретный ключ в словаре перед его доступом?

Должен ли я проверить, присутствует ли конкретный ключ в словаре , если я уверен, что он будет добавлен в словарь, когда я получу код для его доступа?

Есть два способа получить доступ к значению в словаре

  • проверка метода ContainsKey. Если он возвращает true, я получаю доступ с помощью indexer [key] словарного объекта.

или

  1. TryGetValue, который вернет значение true или false, а также значение возвращаемого значения через параметр out.

(второй будет работать лучше, чем 1-й, если я хочу получить значение. Benchmark.)

Однако, если я уверен, что функция, которая обращается к глобальному словарю, наверняка будет иметь ключ, тогда я должен по-прежнему проверять использование TryGetValue или без проверки, я должен использовать indexer [].

Или я никогда не должен предполагать это и всегда проверять?

Ответ 1

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

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

(Также примените рекомендацию Marc о безопасном доступе к общедоступному словарю.)

Ответ 2

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

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

Тем не менее; если вы уверены, что элемент там, индексщик должен быть в порядке:

Foo foo;
lock(syncLock) {
    foo = data[key];
}
// use foo...

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

Foo foo;
lock(syncLock) {
    if(!data.TryGetValue(key, out foo)) {
        foo = new Foo(key);
        data.Add(key, foo);
    }
}
// use foo...

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

Ответ 3

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

СОВЕТ. Если вы решили не проверять, по крайней мере используйте Debug.Assert(dict.ContainsKey(ключ)); Это будет скомпилировано только в режиме Debug, ваша версия сборки не будет содержать его. Таким образом, вы могли бы по крайней мере иметь проверку при отладке.

Еще: если возможно, просто проверьте: -)

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

Ответ 4

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

Ответ 5

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

Если бы я делал систему для управления железнодорожной сигнализацией, я бы хотел быть в безопасности от всех возможных и невозможных ошибок и избежать ошибок в обработке ошибок и т.д. (Мерфи 2-й закон: "что не может пойти не так пойдет не так".) Если я собираюсь собраться вместе ради удовольствия, даже если размер и скорость не будут проблемой, я буду намного более расслаблен в таких вещах - я захочу попасть в забавный материал.

Конечно, иногда это забавный материал сам по себе.

Ответ 6

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

Изменить: Как сказал Джон, если вы знаете, что он всегда будет иметь ключ, тогда вы можете его индексировать и дать ему бросить соответствующее исключение. Однако, если вы можете предоставить лучшую контекстную информацию, бросив ее сами с подробным сообщением, это было бы предпочтительнее.

Ответ 7

Там есть 2 поезда с точки зрения производительности.

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

2) Если вы уверены, что элемент будет существовать там 99% времени, тогда не проверяйте его существование до его доступа. В 1% случаев, когда он не существует, будет выбрано исключение, но вы сохранили время для других 99% времени, не проверяя.

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

Ответ 8

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

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

Ответ 9

Я думаю, что у Марка и Джона это (как обычно) довольно засеяно. Поскольку вы также упоминаете производительность в своем вопросе, возможно, стоит подумать о том, как вы блокируете словарь.

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