Предыстория:
Все мы прекрасно знакомы со стандартным типом Dictionary
Недавно же я разбирал один код, который выдавал 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
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 |
Комментариев нет:
Отправить комментарий