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();
        }
    }
Re: ReadonlyDictionary vs Dictionary
От: Doc Россия http://andrey.moveax.ru
Дата: 12.06.13 09:33
Оценка:
Здравствуйте, Аноним, Вы писали:

А>В чем назначение ReadonlyDictionary ? Какие преимущества он дает ?


Попробуйте добавить элемент в ReadonlyDictionary и все сразу станет ясно.
Re[2]: ReadonlyDictionary vs Dictionary
От: Аноним  
Дата: 12.06.13 09:43
Оценка:
Здравствуйте, Doc, Вы писали:

Doc>Здравствуйте, Аноним, Вы писали:


А>>В чем назначение ReadonlyDictionary ? Какие преимущества он дает ?


Doc>Попробуйте добавить элемент в ReadonlyDictionary и все сразу станет ясно.


Да добавить нельзя, создан только ради этого ? Меньшая функциональность, дольше доступ.
Какой use_casе на практике ?
Re[3]: ReadonlyDictionary vs Dictionary
От: Аноним  
Дата: 12.06.13 09:46
Оценка:
Здравствуйте, Аноним, Вы писали:

[]
А>Какой use_casе на практике ?

Не давать клиентам возможность менять словарь.
Re[3]: ReadonlyDictionary vs Dictionary
От: Doc Россия http://andrey.moveax.ru
Дата: 12.06.13 09:54
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Да добавить нельзя, создан только ради этого ? Меньшая функциональность, дольше доступ.

А>Какой use_casе на практике ?

Выдать клиенту dictionary в который он не сможет дописать и удалять элементы. Например, текущий конфиг.
Re[4]: ReadonlyDictionary vs Dictionary
От: 0x7be СССР  
Дата: 12.06.13 09:54
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Не давать клиентам возможность менять словарь.

Интересно, почему сие было достигнуто классом-оберткой, а не разделением интерфейса.
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 возвращает типизированные значения,
а из словаря нужно будет их преобразовывать в нужный тип.
Re[5]: ReadonlyDictionary vs Dictionary
От: Doc Россия http://andrey.moveax.ru
Дата: 12.06.13 10:17
Оценка:
Здравствуйте, Аноним, Вы писали:

А>А не лучше такие статичные конфиги делать в виде жестких полей ?


По ситуации. Свойствами можно если все ключи конфига известны при создании такого класса.
А если там есть что-то типа секции <appSettings> из внутри может быть сколько угодно каких-то пар "ключ-значение"?

Так что по ситуации.
Re[5]: ReadonlyDictionary vs Dictionary
От: Аноним  
Дата: 12.06.13 12:05
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Здравствуйте, Аноним, Вы писали:


А>>Не давать клиентам возможность менять словарь.

0>Интересно, почему сие было достигнуто классом-оберткой, а не разделением интерфейса.

А как вы себе это представляете?
Объект обычного словаря возвращает интерфейс 'только для чтения'?
Re[5]: ReadonlyDictionary vs Dictionary
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 12.06.13 13:49
Оценка: 6 (1) +1
Здравствуйте, 0x7be, Вы писали:

0>Интересно, почему сие было достигнуто классом-оберткой, а не разделением интерфейса.


Разделение тоже есть. Обычный Dictionary реализует интерфейс IReadOnlyDictionary.
... << RSDN@Home 1.2.0 alpha 5 rev. 99 on Windows 8 6.2.9200.0>>
AVK Blog
Re[3]: ReadonlyDictionary vs Dictionary
От: pavel783  
Дата: 12.06.13 13:50
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Да добавить нельзя, создан только ради этого ? Меньшая функциональность, дольше доступ.

А>Какой use_casе на практике ?

одно название readonly уже нагружает код семантикой, и дает foolproof от изменений извне, это наверно больше полезно для производителей фреймворков и публичных библиотек, может он еще оптимизирован для GC
Re: ReadonlyDictionary vs Dictionary
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 12.06.13 13:54
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>В чем назначение ReadonlyDictionary ? Какие преимущества он дает ?


Я думаю он нужен для того кода, который на вход принимает IDictionary, но при этом не хочется ему давать возможность менять, отлавливая такие попытки хотя бы в рантайме.
... << RSDN@Home 1.2.0 alpha 5 rev. 99 on Windows 8 6.2.9200.0>>
AVK Blog
Re[6]: ReadonlyDictionary vs Dictionary
От: 0x7be СССР  
Дата: 12.06.13 14:50
Оценка:
Здравствуйте, Аноним, Вы писали:

А>А как вы себе это представляете?

А>Объект обычного словаря возвращает интерфейс 'только для чтения'?
Да.
Re: Всё-равно не понятно зачем ReadOnlyDictionary
От: igor-booch Россия  
Дата: 12.06.13 16:29
Оценка: :)
Тут высказывалась версия, что ReadOnlyDictionary нужен чтобы не давать клиентам менять словарь
Автор:
Дата: 12.06.13
. Но это можно достичь только за счет интерфейса IReadOnlyDictionary<TKey, TValue>. Передаешь клиентам переменную, которая имеет тип этого интерфейса и усё. Зачем отдельный класс ReadOnlyDictionary? Так же высказывалась версия, что ReadOnlyDictionary нужен в ситуации когда вызываемый метод хочет IDictionary&lt;TKey, TValue&gt;, но вызывающий код не хочет давать методу менять словарь
Автор: AndrewVK
Дата: 12.06.13
. В пользу этой версии говорит тот факт, что и 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 не нужен, а нужно

interface IDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue> {...}
class     Dictionary<TKey, TValue>  : IDictionary<TKey, TValue>  {...}


У меня есть версия почему MS так не сделала:
недженериковый IDictionary включает свойство IsReadOnly.
В Dictionary в реализации этого метода жестко возвращается false.
В ReadOnlyDictionary true.
Получается класс, который реализует IDictionary и в IsReadOnly возвращает true это не тоже самое, что класс реализующий IReadOnlyDictionary<TKey, TValue>.
Очередная кривизна IMHO.

Может кто-нибудь прояснить?
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[7]: ReadonlyDictionary vs Dictionary
От: Аноним  
Дата: 12.06.13 19:59
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Здравствуйте, Аноним, Вы писали:


А>>А как вы себе это представляете?

А>>Объект обычного словаря возвращает интерфейс 'только для чтения'?
0>Да.

В этом случае хитрый клиент может прикастить интерфейс к IDictionary, и всё-таки изменить содержимое словаря.
Re[8]: ReadonlyDictionary vs Dictionary
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 12.06.13 20:09
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>В этом случае хитрый клиент может прикастить интерфейс к IDictionary, и всё-таки изменить содержимое словаря.


А еще он может залезть через рефлекшен и поменять даже честный ReadOnlyDictionary. Только обычно такие вещи в дизайне не учитывают, потому что сознательные вредители обычно расцениваются как ССЗБ.
... << RSDN@Home 1.2.0 alpha 5 rev. 99 on Windows 8 6.2.9200.0>>
AVK Blog
Re[2]: Всё-равно не понятно зачем ReadOnlyDictionary
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 12.06.13 20:19
Оценка: 4 (1) +2
Здравствуйте, 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>>
AVK Blog
Re[3]: Всё-равно не понятно зачем ReadOnlyDictionary
От: igor-booch Россия  
Дата: 13.06.13 06:31
Оценка:
AVK>Потом они наелись самостоятельно, когда реализовывали розлин
Объясните, пожалуйста поподробнее чего они наелись? Я так понимаю имеется ввиду http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx.

AVK>Огромное же количество кода уже было написано с использованием IDictionary, даже если словарь при этом не менялся.

Почему было недостаточно свойства IDictionary.IsReadOnly ?

AVK>Наследование интерфейсов штука вообще весьма специфичная. Некоторые тут доказывали, что она вообще не нужна.

Дайте пожалуйста ссылку на соответствующее обсуждение.
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[4]: Всё-равно не понятно зачем ReadOnlyDictionary
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 13.06.13 07:57
Оценка:
Здравствуйте, igor-booch, Вы писали:

IB>Объясните, пожалуйста поподробнее чего они наелись?


Плохой поддержки immutable данных в языке и фреймворке.

IB> Я так понимаю имеется ввиду http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx.


Он самый.

AVK>>Наследование интерфейсов штука вообще весьма специфичная. Некоторые тут доказывали, что она вообще не нужна.

IB>Дайте пожалуйста ссылку на соответствующее обсуждение.

Зачем нужно наследование интерфейсов?
Автор: Воронков Василий
Дата: 06.07.12
... << RSDN@Home 1.2.0 alpha 5 rev. 99 on Windows 8 6.2.9200.0>>
AVK Blog
Re[4]: Всё-равно не понятно зачем ReadOnlyDictionary
От: Sinix  
Дата: 13.06.13 08:45
Оценка: +1
Здравствуйте, igor-booch, Вы писали:

IB>Почему было недостаточно свойства IDictionary.IsReadOnly ?

1. Непрактично. Смысл IReadOnlyDictionary — в явной декларации намерений в коде, такие вещи надёжнее выражать через статическую типизацию. Попробуйте сами придумать сценарий использования этого свойства

2. По историческим причинам. В стандартных словарях из BCL (если мы говорим о .Net4 и младше) не было реализации словарей, у которых IsReadOnly возвращал бы true. Соответственно, у нас есть масса кода рассчитанная на изменяемые словари.
Re[2]: Всё-равно не понятно зачем ReadOnlyDictionary
От: SergeyT. США http://sergeyteplyakov.blogspot.com/
Дата: 18.06.13 21:34
Оценка: 55 (4) +1
Здравствуйте, igor-booch, Вы писали:

Смысл существования конкретных неизменяемых коллекций

Если речь идет о внутреннем коде, то вполне достаточно вместо List<T>/Dictionary<,> возвращать их "неизменяемый" интерфейс — IReadOnlyCollection<T>/IReadonlyList<T> или IReadOnlyDictionary<,>, соответственно.
В случае же библиотек, разработчики BCL, вероятно, посчитали такое разделение слишком слабым, поэтому решили ввести возможность завернуть любую список в оболочку с операциями только для чтения.

В подтверждение этой мысли можно привести рекомендацию из Framework Design Guidelines:

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&lt;TKey, TValue&gt;, но вызывающий код не хочет давать методу менять словарь
Автор: AndrewVK
Дата: 12.06.13
. В пользу этой версии говорит тот факт, что и 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 коллекций, что и привело к дополнительным косякам в дизайне.

З.Ы. В процессе подготовки ответа вырисовалась диаграмма классов для коллекций (не всех, но все же):

 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.