Смысл существования конкретных неизменяемых коллекций
Если речь идет о внутреннем коде, то вполне достаточно вместо List<T>/Dictionary<,> возвращать их "неизменяемый" интерфейс — IReadOnlyCollection<T>/IReadonlyList<T> или IReadOnlyDictionary<,>, соответственно.
В случае же библиотек, разработчики BCL, вероятно, посчитали такое разделение слишком слабым, поэтому решили ввести возможность завернуть любую список в оболочку с операциями только для чтения.
DO use ReadonlyCollection<T>, a subclass of ReadOnlyCollection<T>, or in rare cases IEnumerable<T> for properties or return values representing read-only collections.
In general, prefer ReadOnlyCollection<T>. ... In cases where you are sure that the only scenario you will ever want to support is forward-only iteration, you can simply use IEnumerable<T>.
На самом деле, тут есть пара моментов. Как тут уже проскакивало выше, даже неизменяемая коллекция не дает полной гарантии неизменяемости, поскольку всегда можно добраться до "нижележащей" коллекции с помощью рефлексии и изменить ее. С другой стороны, это явный хак, которым будут пользоваться на порядок реже, нежели "динамической" типизацией и привидением типа коллекции от IEnumerable<T> вверх к IList<T>.
Но, опять таки, нужно понимать, что подобная защита (как и приведенный выше совет) на порядок более актуален для библитек и не столь актуален для внутреннего кода. Поэтому в собственном (не библиотечном коде) я обычно возвращаю IEnumerable<T> или соответствующий read-only интерфейс не создавая лишнюю прослойку в виде неизменяемой коллекции.
IB> Так же высказывалась версия, что ReadOnlyDictionary нужен в ситуации когда вызываемый метод хочет IDictionary<TKey, TValue>, но вызывающий код не хочет давать методу менять словарь
. В пользу этой версии говорит тот факт, что и Dictionary и ReadOnlyDictionary реализуют одни и те же интерфейсы:
Это какой-то весьма частный случай, оправданный лишь тогда, когда мы точно знаем, что вызывающий код не меняет переданный словарь и мы этот самый код не можем изменить. Поскольку если это наш код, то нам стоит либо его изменить на использование read-only интерфейса, либо не париться по этому поводу. Передавать же read-only словарь в этот метод в надежде защититься от изменений не лучшая идея, поскольку в этом случае нужно хватать NotSupportedException, что на самом деле является сокрытием багов (что не входит в обязанности обработки исключений).
Почему IDictionary не наследует от IReadOnlyDictionary?
Здесь есть чисто техническое ограничение.
В языке C# не существует возможности переопределить (override) getter свойства, добавив при этом setter. Свойство Item интерфейса IReadOnlyDiction<,> содержит лишь get, и мы не можем нормальным образом добавить наследнику этого интерфейса (т.е. интерфейсу IDictionary<,>) еще и set. Вот простой пример:
interface IBase
{
int Prop {get;}
}
interface IDerived : IBase
{
int Prop {get; set;}
}
class D : IDerived
{
public int Prop {get; set;}
}
class D2 : D, IBase
{
int IBase.Prop {get {return 42;}}
}
void Main()
{
D d = new D2(){Prop = 1};
d.Prop = 1;
d.Prop.Dump(); // 1
IBase b = d;
b.Prop.Dump(); // 42
}
Вместо заключения
В целом, косяков в коллекциях хватает. Сейчас уже никто не скажет, что такое коллекция, поскольку ICollection<T> содержит метод Add, а ICollection — нет; ведь поэтому Collection Initializer использует утиную типизацию.
Также сложно ответить, почему ICollection<T> содержит свойство IsReadOnly, а ICollection — нет. Почему Collection<T> реализует IList<T>, а IReadonlyCollection<T> реализует IReadOnlyList; и почему коллекций использует явную реализацию интерфейсов с бросанием исключения в "неподходящих" методах.
Скорее исторически так сложилось И как правильно написал AndrewVK разработчики BCL слишком поздно поняли важность строготипизированных read-only коллекций, что и привело к дополнительным косякам в дизайне.
З.Ы. В процессе подготовки ответа вырисовалась диаграмма классов для коллекций (не всех, но все же):
Re[2]: Всё-равно не понятно зачем ReadOnlyDictionary
Здравствуйте, igor-booch, Вы писали:
IB>На мой взгляд кривизна какая-то. Если метод хочет IDictionary, значит его функционал может менять словарь
Ты, судя по всему, не в курсе истории вопроса. IDictionary был в фреймворке начиная с версии 2.0. IReadonlyDictionary там нее было, хотя куча народу писали о его необходимости (тут даже кто то с жаром доказывал, что на самом деле IRODictionary низя низя). Потом они наелись самостоятельно, когда реализовывали розлин, и наконец то озаботились добавлением всяких RO фенечек в BCL (и, надеюсь, в C# 6). Но появилось оно только в 4.5. Огромное же количество кода уже было написано с использованием IDictionary, даже если словарь при этом не менялся. Вот для общения с ним и нужен ReadOnlyDictionary. И обрати внимание, не зря он в отдельном неймспейсе расположен.
IB>Еще одна кривизна на мой взгляд заключается в том, что множество членов IReadOnlyDictionary является подмножеством членов IDictionary. Здесь просится наследование интерфейсов
А зачем? Наследование интерфейсов штука вообще весьма специфичная. Некоторые тут доказывали, что она вообще не нужна. Можно пример сценария, где такое наследование оказалось бы полезным?
... << RSDN@Home 1.2.0 alpha 5 rev. 99 on Windows 8 6.2.9200.0>>
. В пользу этой версии говорит тот факт, что и Dictionary и ReadOnlyDictionary реализуют одни и те же интерфейсы:
class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue> {...}
class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue> {...}
В ReadOnlyDictionary методы IDictionary.Add и IDictionary.Remove спрятаны за счет явной реализации интерфейса + в их реализации выкидывается эксепшен.
На мой взгляд кривизна какая-то. Если метод хочет IDictionary, значит его функционал может менять словарь, если функционал метода не меняет словарь пускай требует только IReadOnlyDictionary. Если функционал метода может менять словарь словарь, а Вы ему даете ReadOnlyDictionary, то метод не сможет выполнить свои обязанности. Возникает вопрос зачем вам тогда вообще вызывать этот метод.
Еще одна кривизна на мой взгляд заключается в том, что множество членов IReadOnlyDictionary является подмножеством членов IDictionary. Здесь просится наследование интерфейсов, но нет, общие методы IDictionary<TKey, TValue> и IReadOnlyDictionary<TKey, TValue> продублированы. По мне класс ReadOnlyDictionary не нужен, а нужно
У меня есть версия почему MS так не сделала:
недженериковый IDictionary включает свойство IsReadOnly.
В Dictionary в реализации этого метода жестко возвращается false.
В ReadOnlyDictionary true.
Получается класс, который реализует IDictionary и в IsReadOnly возвращает true это не тоже самое, что класс реализующий IReadOnlyDictionary<TKey, TValue>.
Очередная кривизна IMHO.
Может кто-нибудь прояснить?
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
Re[4]: Всё-равно не понятно зачем ReadOnlyDictionary
Здравствуйте, igor-booch, Вы писали:
IB>Почему было недостаточно свойства IDictionary.IsReadOnly ?
1. Непрактично. Смысл IReadOnlyDictionary — в явной декларации намерений в коде, такие вещи надёжнее выражать через статическую типизацию. Попробуйте сами придумать сценарий использования этого свойства
2. По историческим причинам. В стандартных словарях из BCL (если мы говорим о .Net4 и младше) не было реализации словарей, у которых IsReadOnly возвращал бы true. Соответственно, у нас есть масса кода рассчитанная на изменяемые словари.
ReadonlyDictionary vs Dictionary
От:
Аноним
Дата:
12.06.13 09:26
Оценка:
В чем назначение ReadonlyDictionary ? Какие преимущества он дает ?
Также насколько правильным является такой тест для демонстрации возможностей ReadonlyDictionary, почему-то результат не особо в пользу ReadonlyDictionary.
Dictionary Elements 5 : 126 ms
ReadOnlyDictionary Elements 5 : 139 ms
Dictionary Elements 50 : 1305 ms
ReadOnlyDictionary Elements 50 : 1345 ms
Dictionary Elements 500 : 12357 ms
ReadOnlyDictionary Elements 500 : 13039 ms
Dictionary Elements 5000: 126129 ms
ReadOnlyDictionary Elements 5000: 134018 ms
Вот код теста
class Program
{
static Dictionary<int, int> d5 = CreateDictionary(5);
static ReadOnlyDictionary<int, int> rod5 = new ReadOnlyDictionary<int,int>(d5);
static Dictionary<int, int> d50 = CreateDictionary(50);
static ReadOnlyDictionary<int, int> rod50 = new ReadOnlyDictionary<int, int>(d50);
static Dictionary<int, int> d500 = CreateDictionary(500);
static ReadOnlyDictionary<int, int> rod500 = new ReadOnlyDictionary<int, int>(d500);
static Dictionary<int, int> d5000 = CreateDictionary(5000);
static ReadOnlyDictionary<int, int> rod5000 = new ReadOnlyDictionary<int, int>(d5000);
static Dictionary<int, int> CreateDictionary(int elements)
{
var dict = new Dictionary<int,int>(elements);
for (int i = 0; i < elements;i++ )
dict[i + 1] = i + 1;
return dict;
}
static void TestDictionary(IDictionary<int, int> dict)
{
var sw = new Stopwatch();
sw.Start();
Int64 sum = 0;
int size = dict.Count;
for (int i = 0; i < 1000000; i++)
for(int j = 1; j<=size; j++)
sum += dict[j];
sw.Stop();
Console.WriteLine("{0} Elements {2} : {1} ms", dict.GetType().Name, sw.ElapsedMilliseconds, dict.Count);
}
static void Main(string[] args)
{
TestDictionary(d5);
TestDictionary(rod5);
TestDictionary(d50);
TestDictionary(rod50);
TestDictionary(d500);
TestDictionary(rod500);
TestDictionary(d5000);
TestDictionary(rod5000);
Console.ReadKey();
}
}
Здравствуйте, Аноним, Вы писали:
А>В чем назначение ReadonlyDictionary ? Какие преимущества он дает ?
Попробуйте добавить элемент в ReadonlyDictionary и все сразу станет ясно.
Re[2]: ReadonlyDictionary vs Dictionary
От:
Аноним
Дата:
12.06.13 09:43
Оценка:
Здравствуйте, Doc, Вы писали:
Doc>Здравствуйте, Аноним, Вы писали:
А>>В чем назначение ReadonlyDictionary ? Какие преимущества он дает ?
Doc>Попробуйте добавить элемент в ReadonlyDictionary и все сразу станет ясно.
Да добавить нельзя, создан только ради этого ? Меньшая функциональность, дольше доступ.
Какой use_casе на практике ?
Здравствуйте, Аноним, Вы писали:
А>Да добавить нельзя, создан только ради этого ? Меньшая функциональность, дольше доступ. А>Какой use_casе на практике ?
Выдать клиенту dictionary в который он не сможет дописать и удалять элементы. Например, текущий конфиг.
Здравствуйте, Аноним, Вы писали:
А>Не давать клиентам возможность менять словарь.
Интересно, почему сие было достигнуто классом-оберткой, а не разделением интерфейса.
Re[4]: ReadonlyDictionary vs Dictionary
От:
Аноним
Дата:
12.06.13 09:57
Оценка:
Здравствуйте, Doc, Вы писали:
Doc>Здравствуйте, Аноним, Вы писали:
А>>Да добавить нельзя, создан только ради этого ? Меньшая функциональность, дольше доступ. А>>Какой use_casе на практике ?
Doc>Выдать клиенту dictionary в который он не сможет дописать и удалять элементы. Например, текущий конфиг.
А не лучше такие статичные конфиги делать в виде жестких полей ?
Вместо
IDictionary<string,string>
давать
public class Config
{
public string Path1 { get; }
public int Timeout { get; }
}
ведь
dictionary["Очепятка"] обнаружится только в Runtime,
а
Config.Очепятка на этапе компиляции + Config возвращает типизированные значения,
а из словаря нужно будет их преобразовывать в нужный тип.
Здравствуйте, Аноним, Вы писали:
А>А не лучше такие статичные конфиги делать в виде жестких полей ?
По ситуации. Свойствами можно если все ключи конфига известны при создании такого класса.
А если там есть что-то типа секции <appSettings> из внутри может быть сколько угодно каких-то пар "ключ-значение"?
Так что по ситуации.
Re[5]: ReadonlyDictionary vs Dictionary
От:
Аноним
Дата:
12.06.13 12:05
Оценка:
Здравствуйте, 0x7be, Вы писали:
0>Здравствуйте, Аноним, Вы писали:
А>>Не давать клиентам возможность менять словарь. 0>Интересно, почему сие было достигнуто классом-оберткой, а не разделением интерфейса.
А как вы себе это представляете?
Объект обычного словаря возвращает интерфейс 'только для чтения'?
Здравствуйте, Аноним, Вы писали:
А>Да добавить нельзя, создан только ради этого ? Меньшая функциональность, дольше доступ. А>Какой use_casе на практике ?
одно название readonly уже нагружает код семантикой, и дает foolproof от изменений извне, это наверно больше полезно для производителей фреймворков и публичных библиотек, может он еще оптимизирован для GC
Здравствуйте, <Аноним>, Вы писали:
А>В чем назначение ReadonlyDictionary ? Какие преимущества он дает ?
Я думаю он нужен для того кода, который на вход принимает IDictionary, но при этом не хочется ему давать возможность менять, отлавливая такие попытки хотя бы в рантайме.
... << RSDN@Home 1.2.0 alpha 5 rev. 99 on Windows 8 6.2.9200.0>>
Здравствуйте, Аноним, Вы писали:
А>А как вы себе это представляете? А>Объект обычного словаря возвращает интерфейс 'только для чтения'?
Да.
Re[7]: ReadonlyDictionary vs Dictionary
От:
Аноним
Дата:
12.06.13 19:59
Оценка:
Здравствуйте, 0x7be, Вы писали:
0>Здравствуйте, Аноним, Вы писали:
А>>А как вы себе это представляете? А>>Объект обычного словаря возвращает интерфейс 'только для чтения'? 0>Да.
В этом случае хитрый клиент может прикастить интерфейс к IDictionary, и всё-таки изменить содержимое словаря.
Здравствуйте, <Аноним>, Вы писали:
А>В этом случае хитрый клиент может прикастить интерфейс к IDictionary, и всё-таки изменить содержимое словаря.
А еще он может залезть через рефлекшен и поменять даже честный ReadOnlyDictionary. Только обычно такие вещи в дизайне не учитывают, потому что сознательные вредители обычно расцениваются как ССЗБ.
... << RSDN@Home 1.2.0 alpha 5 rev. 99 on Windows 8 6.2.9200.0>>
AVK>Потом они наелись самостоятельно, когда реализовывали розлин
Объясните, пожалуйста поподробнее чего они наелись? Я так понимаю имеется ввиду http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx.
AVK>Огромное же количество кода уже было написано с использованием IDictionary, даже если словарь при этом не менялся.
Почему было недостаточно свойства IDictionary.IsReadOnly ?
AVK>Наследование интерфейсов штука вообще весьма специфичная. Некоторые тут доказывали, что она вообще не нужна.
Дайте пожалуйста ссылку на соответствующее обсуждение.
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
Re[4]: Всё-равно не понятно зачем ReadOnlyDictionary
Он самый.
AVK>>Наследование интерфейсов штука вообще весьма специфичная. Некоторые тут доказывали, что она вообще не нужна. IB>Дайте пожалуйста ссылку на соответствующее обсуждение.