标题很基本,我为什么不能:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
答案 0 :(得分:61)
对原始问题的评论很好地总结了这一点:
因为没有人设计,指定,实施,测试,记录和发送该功能。 - @Gabe Moothart
至于为什么?好吧,可能是因为合并字典的行为无法以符合框架指南的方式进行推理。
AddRange
不存在,因为范围对关联容器没有任何意义,因为数据范围允许重复条目。例如,如果您有IEnumerable<KeyValuePair<K,T>>
该集合不能防止重复输入。
添加一组键值对,甚至合并两个词典的行为是直截了当的。但是,如何处理多个重复条目的行为不是。
该方法处理副本时的行为应该是什么?
我能想到至少有三种解决方案:
当抛出异常时,原始字典的状态应该是什么?
Add
几乎总是作为原子操作实现:它成功并更新集合的状态,或者它失败,并且集合的状态保持不变。由于重复错误AddRange
可能会失败,因此保持其行为与Add
一致的方法也是通过在任何副本上抛出异常使其成为原子,并将原始字典的状态保留为不变。
作为API使用者,必须迭代删除重复元素是很繁琐的,这意味着AddRange
应该抛出一个包含 all 重复值的异常。
然后选择归结为:
有支持这两种用例的论据。为此,您是否在签名中添加了IgnoreDuplicates
标志?
IgnoreDuplicates
标志(设置为true时)也会显着加快速度,因为底层实现会绕过重复检查的代码。
所以现在,你有一个标志,允许AddRange
支持这两种情况,但是有一个无证的副作用(这是框架设计者很难避免的)。
<强>摘要强>
由于在处理重复项时没有明确,一致和预期的行为,因此不容易将它们全部处理在一起,并且不提供开始的方法。
如果您发现自己不得不合并字典,您当然可以编写自己的扩展方法来合并字典,这些字典的行为方式适用于您的应用程序。
答案 1 :(得分:24)
找到了另一种解决方案:
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 void AddRangeOverride<TKey, TValue>(this Dictionary<TKey, TValue> dic, Dictionary<TKey, TValue> dicToAdd)
{
dicToAdd.ForEach(x => dic[x.Key] = x.Value);
}
public static void AddRangeNewOnly<TKey, TValue>(this Dictionary<TKey, TValue> dic, Dictionary<TKey, TValue> dicToAdd)
{
dicToAdd.ForEach(x => { if (!dic.ContainsKey(x.Key)) dic.Add(x.Key, x.Value); });
}
public static void AddRange<TKey, TValue>(this Dictionary<TKey, TValue> dic, Dictionary<TKey, TValue> dicToAdd)
{
dicToAdd.ForEach(x => dic.Add(x.Key, x.Value));
}
public static bool ContainsKeys<TKey, TValue>(this Dictionary<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;
}
}
}
玩得开心。
答案 2 :(得分:12)
我的猜测是,对于发生的事情,用户缺乏适当的输出。 由于你不能在字典中重复键,你将如何处理合并两个字典,其中一些键相交?当然你可以说:“我不在乎”,但这违反了返回false /抛出重复键异常的惯例。
答案 3 :(得分:9)
如果有人像我一样遇到这个问题 - 可以实现&#34; AddRange&#34;通过使用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())
部分。在这种情况下,它只是从重复组中获取第一个元素,但如果需要,您可以在那里实现更复杂的逻辑。
答案 4 :(得分:6)
你可以这样做
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 for addrange和/或使用上面的模式。
List<KeyValuePair<string, string>>
答案 5 :(得分:1)
如果你正在处理一个新的词典(并且你没有丢失现有的行),你总是可以从另一个对象列表中使用ToDictionary()。
所以,在你的情况下,你会做这样的事情:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic = SomeList.ToDictionary(x => x.Attribute1, x => x.Attribute2);
答案 6 :(得分:1)
随意使用扩展方法,如下所示:
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 :(得分:0)
如果你知道你不会有重复的密钥,你可以这样做:
dic = dic.Union(MethodThatReturnAnotherDic()).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
如果存在重复的键/值对,则会抛出异常。
我不知道为什么这不在框架中;应该。没有不确定性;只是抛出一个例外。在这个代码的情况下,它确实抛出异常。
答案 8 :(得分:0)
这是使用c#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);
答案 9 :(得分:0)
正如其他人提到的那样,未实现Dictionary<TKey,TVal>.AddRange
的原因是,您可能想通过多种方式来处理重复项。 Collection
或IDictionary<TKey,TVal>
,ICollection<T>
等接口也是如此。
仅List<T>
实现了该功能,出于相同的原因,您会注意到IList<T>
接口没有实现:在将一系列值添加到列表中时预期行为收集内容可能会因背景而异。
问题的上下文表明您不担心重复,在这种情况下,您可以使用Linq使用简单的oneliner替代方法:
MethodThatReturnAnotherDic().ToList.ForEach(kvp => dic.Add(kvp.Key, kvp.Value));
答案 10 :(得分:0)
只需使用Concat()
:
dic.Concat(MethodThatReturnAnotherDic());