Название достаточно простое, почему я не могу:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
c#
.net
dictionary
Custodio
источник
источник
Ответы:
Комментарий к исходному вопросу довольно хорошо резюмирует:
А почему? Что ж, вероятно, потому, что поведение слияния словарей не может быть рассмотрено таким образом, чтобы соответствовать рекомендациям Framework.
AddRange
не существует, потому что диапазон не имеет значения для ассоциативного контейнера, поскольку диапазон данных допускает повторяющиеся записи. Например, если у вас есть такаяIEnumerable<KeyValuePair<K,T>>
коллекция, это не защищает от дублирования записей.Поведение при добавлении коллекции пар ключ-значение или даже при слиянии двух словарей является простым. Однако поведение того, как работать с несколькими повторяющимися записями, не так.
Каким должно быть поведение метода при работе с дубликатом?
Я могу придумать как минимум три решения:
В каком состоянии должен находиться исходный словарь при возникновении исключения?
Add
почти всегда реализуется как атомарная операция: она завершается успешно и обновляет состояние коллекции или терпит неудачу, а состояние коллекции остается неизменным. ПосколькуAddRange
может произойти сбой из-за повторяющихся ошибок, способ сохранить его поведение согласованным сAdd
тем, чтобы также сделать его атомарным, выбрасывая исключение для любого дубликата и оставив состояние исходного словаря неизменным.Для потребителя API было бы утомительно итеративно удалять повторяющиеся элементы, что означает, что
AddRange
объект должен генерировать единственное исключение, содержащее все повторяющиеся значения.Тогда выбор сводится к следующему:
Есть аргументы в пользу обоих вариантов использования. Для этого нужно добавить
IgnoreDuplicates
к подписи флажок?IgnoreDuplicates
Флаг (если установлен верно) также обеспечит большую скорость вверх, в качестве базовой реализации обойдет код для дубликата проверки.Итак, теперь у вас есть флаг, который позволяет
AddRange
поддерживать оба случая, но имеет недокументированный побочный эффект (чего разработчики Framework очень старались избежать).Резюме
Поскольку нет четкого, последовательного и ожидаемого поведения, когда дело доходит до работы с дубликатами, легче не обрабатывать их все вместе и не предоставлять метод для начала.
Если вам постоянно приходится объединять словари, вы, конечно, можете написать свой собственный метод расширения для объединения словарей, который будет вести себя так, как это работает для вашего приложения (приложений).
источник
AddMultiple
отличается от этогоAddRange
, независимо от того, что его реализация будет нестабильной: вы генерируете исключение с массивом всех повторяющихся ключей? Или вы создаете исключение для первого встречающегося дубликата ключа? Каким должно быть состояние словаря при возникновении исключения? Безупречный или все ключи, которые удались?Add
- либо заключить каждыйAdd
в atry...catch
и таким образом перехватить дубликаты; или используйте индексатор и замените первое значение более поздним значением; или предварительно проверьте использованиеContainsKey
перед попыткойAdd
, сохранив таким образом исходное значение. Если бы у фреймворка был методAddRange
илиAddMultiple
, единственный простой способ сообщить о том, что произошло, - через исключение, а обработка и восстановление были бы не менее сложными.У меня есть решение:
...
Радоваться, веселиться.
источник
ToList()
, словарь - этоIEnumerable<KeyValuePair<TKey,TValue>
. Кроме того, второй и третий методы будут выбрасываться, если вы добавите существующее значение ключа. Плохая идея, вы искалиTryAdd
? Наконец, второй может быть заменен наWhere(pair->!dic.ContainsKey(pair.Key)...
ToList()
это не лучшее решение, поэтому я изменил код. Вы можете использовать,try { mainDic.AddRange(addDic); } catch { do something }
если не уверены в третьем методе. Второй способ работает отлично.Если кто-то, как и я, сталкивается с этим вопросом - «AddRange» можно достичь, используя методы расширения IEnumerable:
Основная хитрость при объединении словарей - это дублирование ключей. В приведенном выше коде это часть
.Select(grp => grp.First())
. В этом случае он просто берет первый элемент из группы дубликатов, но при необходимости вы можете реализовать более сложную логику.источник
dict1
не используется компаратор проверки на равенство по умолчанию?var combined = dict1.Concat(dict2).GroupBy(kvp => kvp.Key, dict1.Comparer).ToDictionary(grp => grp.Key, grp=> grp.First(), dict1.Comparer);
Я предполагаю, что пользователю не хватает информации о том, что произошло. Поскольку у вас не может быть повторяющихся ключей в словарях, как бы вы справились с объединением двух словарей, где некоторые ключи пересекаются? Конечно, вы могли бы сказать: «Мне все равно», но это нарушает соглашение о возврате false / выдаче исключения для повторяющихся ключей.
источник
Add
, кроме того, это может происходить более одного раза. Он бы бросил то же,ArgumentException
чтоAdd
делает, неужели?NamedElementException
??), либо брошенным вместо, либо как внутреннее исключение исключения ArgumentException, которое указывает именованный элемент, который конфликтует ... несколько разных вариантов, я бы сказалТы мог бы сделать это
или используйте список для добавления диапазона и / или используя шаблон выше.
источник
MethodThatReturnAnotherDic
. Это происходит из ОП. Пожалуйста, еще раз просмотрите вопрос и мой ответ.Если вы имеете дело с новым словарем (и у вас нет существующих строк, которые можно потерять), вы всегда можете использовать ToDictionary () из другого списка объектов.
Итак, в вашем случае вы бы сделали что-то вроде этого:
источник
Dictionary<string, string> dic = SomeList.ToDictionary...
Если вы знаете, что у вас не будет дублирующихся ключей, вы можете:
Это вызовет исключение, если есть повторяющаяся пара ключ / значение.
Я не знаю, почему этого нет в рамках; должно быть. Нет никакой неопределенности; просто выбросьте исключение. В случае с этим кодом возникает исключение.
источник
var caseInsensitiveDictionary = new Dictionary<string, int>( StringComparer.OrdinalIgnoreCase);
?Не стесняйтесь использовать такой метод расширения:
источник
Вот альтернативное решение с использованием С # 7 ValueTuples (литералы кортежей)
Используется как
источник
Как уже упоминали другие, причина, по которой
Dictionary<TKey,TVal>.AddRange
не реализовано, заключается в том, что существуют различные способы обработки случаев, когда у вас есть дубликаты. Это также касаетсяCollection
или интерфейсов , таких какIDictionary<TKey,TVal>
,ICollection<T>
и т.д.Только
List<T>
реализует его, и вы заметите, чтоIList<T>
интерфейс этого не делает по тем же причинам: ожидаемый поведение при добавлении диапазона значений в коллекцию может широко варьироваться в зависимости от контекста.Контекст вашего вопроса предполагает, что вы не беспокоитесь о дубликатах, и в этом случае у вас есть простая альтернатива oneeliner с использованием Linq:
источник