Страницы

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

среда, 15 мая 2019 г.

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

Предыстория:
Все мы прекрасно знакомы со стандартным типом Dictionary, а также его свойством, что при попытке доступа к элементу по несуществующему ключу будет создано исключение KeyNotFoundException Это привычно для всех и подобное поведение ожидаемо для словаря значений
Недавно же я разбирал один код, который выдавал NullReferenceException там, где его по логике вещей быть не могло.
Отдебажив его, я обнаружил следующую вещь: Человек использовал в своем коде StringDictionary (к слову, весьма радует, ибо я знаю внушающее количество .NET программистов, которые ни разу не заглядывали в System.Collections.Specialized) и забыл поставить проверку на существование строки по указанному ключу, тем самым словарь молча возвращал null и чуть позже код падал, так как строки со значением null должны были отсутствовать внутри словаря и проверка на это не подразумевалась
В случае со стандартным Dictionary и созданием указанного выше исключения эту ошибку отловить было бы куда проще, однако у StringDictionary немного иная "идеология", так что по несуществующему ключу он возвращает null (который, к слову, без вызова метода ContainsKey неотличим от существующей по указанному ключу строки с null-значением)

Собственно, сам вопрос:
Почему при проектировании StringDictionary отошли от архитектуры привычного всем словаря и вместо логичного выброса соответствующего исключения сделали просто возвращение null?
Мне кажется, что подобное решение должно быть подкреплено какими-то серьезными фактами. Из моих догадок: так как StringDictionary создан для быстрого (O(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 |

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

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