Название достаточно простое, почему я не могу:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
Название достаточно простое, почему я не могу:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
Ответы:
Комментарий к исходному вопросу довольно хорошо резюмирует:
потому что никто никогда не проектировал, не специфицировал, не реализовывал, не тестировал, не документировал и не поставлял эту функцию. - @Gabe Moothart
А почему? Что ж, вероятно, потому, что поведение слияния словарей не может быть рассмотрено таким образом, чтобы соответствовать рекомендациям Framework.
AddRangeне существует, потому что диапазон не имеет значения для ассоциативного контейнера, поскольку диапазон данных допускает повторяющиеся записи. Например, если у вас есть такая IEnumerable<KeyValuePair<K,T>>коллекция, это не защищает от дублирования записей.
Поведение при добавлении коллекции пар ключ-значение или даже при слиянии двух словарей является простым. Однако поведение того, как работать с несколькими повторяющимися записями, не так.
Каким должно быть поведение метода при работе с дубликатом?
Я могу придумать как минимум три решения:
В каком состоянии должен находиться исходный словарь при возникновении исключения?
Addпочти всегда реализуется как атомарная операция: она завершается успешно и обновляет состояние коллекции или терпит неудачу, а состояние коллекции остается неизменным. Поскольку AddRangeможет произойти сбой из-за повторяющихся ошибок, способ сохранить его поведение согласованным с Addтем, чтобы также сделать его атомарным, выбрасывая исключение для любого дубликата и оставив состояние исходного словаря неизменным.
Для потребителя API было бы утомительно итеративно удалять повторяющиеся элементы, что означает, что AddRangeобъект должен генерировать единственное исключение, содержащее все повторяющиеся значения.
Тогда выбор сводится к следующему:
Есть аргументы в пользу обоих вариантов использования. Для этого нужно добавить IgnoreDuplicatesк подписи флажок?
IgnoreDuplicatesФлаг (если установлен верно) также обеспечит большую скорость вверх, в качестве базовой реализации обойдет код для дубликата проверки.
Итак, теперь у вас есть флаг, который позволяет AddRangeподдерживать оба случая, но имеет недокументированный побочный эффект (чего разработчики Framework очень старались избежать).
Резюме
Поскольку нет четкого, последовательного и ожидаемого поведения, когда дело доходит до работы с дубликатами, легче не обрабатывать их все вместе и не предоставлять метод для начала.
Если вам постоянно приходится объединять словари, вы, конечно, можете написать свой собственный метод расширения для объединения словарей, который будет вести себя так, как это работает для вашего приложения (приложений).
AddMultipleотличается от этого AddRange, независимо от того, что его реализация будет нестабильной: вы генерируете исключение с массивом всех повторяющихся ключей? Или вы создаете исключение для первого встречающегося дубликата ключа? Каким должно быть состояние словаря при возникновении исключения? Безупречный или все ключи, которые удались?
Add- либо заключить каждый Addв a try...catchи таким образом перехватить дубликаты; или используйте индексатор и замените первое значение более поздним значением; или предварительно проверьте использование ContainsKeyперед попыткой Add, сохранив таким образом исходное значение. Если бы у фреймворка был метод AddRangeили AddMultiple, единственный простой способ сообщить о том, что произошло, - через исключение, а обработка и восстановление были бы не менее сложными.
У меня есть решение:
Dictionary<string, string> mainDic = new Dictionary<string, string>() {
{ "Key1", "Value1" },
{ "Key2", "Value2.1" },
};
Dictionary<string, string> additionalDic= new Dictionary<string, string>() {
{ "Key2", "Value2.2" },
{ "Key3", "Value3" },
};
mainDic.AddRangeOverride(additionalDic); // Overrides all existing keys
// or
mainDic.AddRangeNewOnly(additionalDic); // Adds new keys only
// or
mainDic.AddRange(additionalDic); // Throws an error if keys already exist
// or
if (!mainDic.ContainsKeys(additionalDic.Keys)) // Checks if keys don't exist
{
mainDic.AddRange(additionalDic);
}
...
namespace MyProject.Helper
{
public static class CollectionHelper
{
public static void AddRangeOverride<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
{
dicToAdd.ForEach(x => dic[x.Key] = x.Value);
}
public static void AddRangeNewOnly<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
{
dicToAdd.ForEach(x => { if (!dic.ContainsKey(x.Key)) dic.Add(x.Key, x.Value); });
}
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
{
dicToAdd.ForEach(x => dic.Add(x.Key, x.Value));
}
public static bool ContainsKeys<TKey, TValue>(this IDictionary<TKey, TValue> dic, IEnumerable<TKey> keys)
{
bool result = false;
keys.ForEachOrBreak((x) => { result = dic.ContainsKey(x); return result; });
return result;
}
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var item in source)
action(item);
}
public static void ForEachOrBreak<T>(this IEnumerable<T> source, Func<T, bool> func)
{
foreach (var item in source)
{
bool result = func(item);
if (result) break;
}
}
}
}
Радоваться, веселиться.
ToList(), словарь - это IEnumerable<KeyValuePair<TKey,TValue>. Кроме того, второй и третий методы будут выбрасываться, если вы добавите существующее значение ключа. Плохая идея, вы искали TryAdd? Наконец, второй может быть заменен наWhere(pair->!dic.ContainsKey(pair.Key)...
ToList()это не лучшее решение, поэтому я изменил код. Вы можете использовать, try { mainDic.AddRange(addDic); } catch { do something }если не уверены в третьем методе. Второй способ работает отлично.
Если кто-то, как и я, сталкивается с этим вопросом - «AddRange» можно достичь, используя методы расширения IEnumerable:
var combined =
dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.Select(grp => grp.First())
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Основная хитрость при объединении словарей - это дублирование ключей. В приведенном выше коде это часть .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, которое указывает именованный элемент, который конфликтует ... несколько разных вариантов, я бы сказал
Ты мог бы сделать это
Dictionary<string, string> dic = new Dictionary<string, string>();
// dictionary other items already added.
MethodThatReturnAnotherDic(dic);
public void MethodThatReturnAnotherDic(Dictionary<string, string> dic)
{
dic.Add(.., ..);
}
или используйте список для добавления диапазона и / или используя шаблон выше.
List<KeyValuePair<string, string>>
MethodThatReturnAnotherDic. Это происходит из ОП. Пожалуйста, еще раз просмотрите вопрос и мой ответ.
Если вы имеете дело с новым словарем (и у вас нет существующих строк, которые можно потерять), вы всегда можете использовать ToDictionary () из другого списка объектов.
Итак, в вашем случае вы бы сделали что-то вроде этого:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic = SomeList.ToDictionary(x => x.Attribute1, x => x.Attribute2);
Dictionary<string, string> dic = SomeList.ToDictionary...
Если вы знаете, что у вас не будет дублирующихся ключей, вы можете:
dic = dic.Union(MethodThatReturnAnotherDic()).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Это вызовет исключение, если есть повторяющаяся пара ключ / значение.
Я не знаю, почему этого нет в рамках; должно быть. Нет никакой неопределенности; просто выбросьте исключение. В случае с этим кодом возникает исключение.
var caseInsensitiveDictionary = new Dictionary<string, int>( StringComparer.OrdinalIgnoreCase);?
Не стесняйтесь использовать такой метод расширения:
public static Dictionary<T, U> AddRange<T, U>(this Dictionary<T, U> destination, Dictionary<T, U> source)
{
if (destination == null) destination = new Dictionary<T, U>();
foreach (var e in source)
destination.Add(e.Key, e.Value);
return destination;
}
Вот альтернативное решение с использованием С # 7 ValueTuples (литералы кортежей)
public static class DictionaryExtensions
{
public static Dictionary<TKey, TValue> AddRange<TKey, TValue>(this Dictionary<TKey, TValue> source, IEnumerable<ValueTuple<TKey, TValue>> kvps)
{
foreach (var kvp in kvps)
source.Add(kvp.Item1, kvp.Item2);
return source;
}
public static void AddTo<TKey, TValue>(this IEnumerable<ValueTuple<TKey, TValue>> source, Dictionary<TKey, TValue> target)
{
target.AddRange(source);
}
}
Используется как
segments
.Zip(values, (s, v) => (s.AsSpan().StartsWith("{") ? s.Trim('{', '}') : null, v))
.Where(zip => zip.Item1 != null)
.AddTo(queryParams);
Как уже упоминали другие, причина, по которой Dictionary<TKey,TVal>.AddRangeне реализовано, заключается в том, что существуют различные способы обработки случаев, когда у вас есть дубликаты. Это также касается Collectionили интерфейсов , таких как IDictionary<TKey,TVal>, ICollection<T>и т.д.
Только List<T>реализует его, и вы заметите, что IList<T>интерфейс этого не делает по тем же причинам: ожидаемый поведение при добавлении диапазона значений в коллекцию может широко варьироваться в зависимости от контекста.
Контекст вашего вопроса предполагает, что вы не беспокоитесь о дубликатах, и в этом случае у вас есть простая альтернатива oneeliner с использованием Linq:
MethodThatReturnAnotherDic().ToList.ForEach(kvp => dic.Add(kvp.Key, kvp.Value));