Страницы

Поиск по вопросам

пятница, 28 февраля 2020 г.

Особенности архитектуры StringDictionary

#net #словари


Предыстория:

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

Недавно же я разбирал один код, который выдавал NullReferenceException там, где его
по логике вещей быть не могло.

Отдебажив его, я обнаружил следующую вещь:
Человек использовал в своем коде StringDictionary (к слову, весьма радует, ибо я
знаю внушающее количество .NET программистов, которые ни разу не заглядывали в System.Collections.Specialized)
и забыл поставить проверку на существование строки по указанному ключу, тем самым словарь
молча возвращал null и чуть позже код падал, так как строки со значением null должны
были отсутствовать внутри словаря и проверка на это не подразумевалась

В случае со стандартным Dictionary и созданием указанного выше исключения эту ошибку
отловить было бы куда проще, однако у StringDictionary немного иная "идеология", так
что по несуществующему ключу он возвращает null (который, к слову, без вызова метода
ContainsKey неотличим от существующей по указанному ключу строки с null-значением)



Собственно, сам вопрос:

Почему при проектировании StringDictionary отошли от архитектуры привычного всем
словаря и вместо логичного выброса соответствующего исключения сделали просто возвращение
null?

Мне кажется, что подобное решение должно быть подкреплено какими-то серьезными фактами.
Из моих догадок: так как StringDictionary создан для быстрого (O(1)) поиска строк в
словаре, то есть скорость в данном случае критична, то выбросом исключений решили пренебречь,
так как это не самая быстрая операция)
Однако это лишь моя догадка, хотелось бы услышать Ваше мнение/увидеть ссылки по данному
вопросу)
    


Ответы

Ответ 1



Все гораздо проще - Specialized - это рудимент до-генериковского .net 1.1. Генерик-реализация Dictionary не может возвращать null для несществующих значений - она должна работать и с reference types, и с value types, причем работать одинаково. Вернуть же null для value type несоколько затруднительно. На момент появления генериков классы из Specialized активно использовались, и их поведение сохранили ради совместимости. В современном проекте единственая причина для работы с Specialized - это необходимость интеграции с legacy кодом. Они не реализуют новых генерик интерфейсов, они ведут себя странно и непредсказуемо (например, StringDictionary приводит ключи к lowercase), и они могут быть медленее новых генериков даже на базовых операциях, например: [Benchmark] public StringDictionary StringDictionary() { StringDictionary stringDictionary = new StringDictionary(); foreach (var i in Enumerable.Range(0, 1000000)) { stringDictionary[i.ToString()] = i.ToString(); } return stringDictionary; } [Benchmark] public Dictionary NewDictionary() { var stringDictionary = new Dictionary(); foreach (var i in Enumerable.Range(0, 1000000)) { stringDictionary[i.ToString()] = i.ToString(); } return stringDictionary; } Method | Mean | Error | StdDev | ----------------- |---------:|---------:|---------:| StringDictionary | 694.0 ms | 5.631 ms | 5.268 ms | NewDictionary | 280.2 ms | 5.369 ms | 5.022 ms |

Комментариев нет:

Отправить комментарий