c# - 为什么Dictionary没有AddRange?

标题是基本的,为什么我不能:

Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
Custodio asked 2019-08-13T14:18:06Z
8个解决方案
60 votes

对原始问题的评论很好地总结了这一点:

因为没有人设计,指定,实施,测试,记录和发送该功能。 - @Gabe Moothart

至于为什么? 好吧,可能是因为合并字典的行为不能以符合框架指南的方式进行推理。

AddRange不存在,因为范围对关联容器没有任何意义,因为数据范围允许重复条目。 例如,如果您有AddRange,该集合不能防止重复输入。

添加键值对的集合,甚至合并两个词典的行为是直截了当的。 但是,如何处理多个重复条目的行为不是。

处理重复时该方法的行为应该是什么?

我能想到至少有三种解决方案:

  1. 为第一个重复的条目抛出异常
  2. 抛出包含所有重复条目的异常
  3. 忽略重复

抛出异常时,原始字典的状态应该是什么?

AddRange几乎总是作为原子操作实现:它成功并更新集合的状态,或者它失败,并且集合的状态保持不变。 由于重复错误导致AddRange失败,因此保持其行为与Add一致的方法也是通过在任何副本上抛出异常使其成为原子,并使原始字典的状态保持不变。

作为API使用者,必须迭代地删除重复元素将是繁琐的,这意味着AddRange应该抛出包含所有重复值的单个异常。

然后选择归结为:

  1. 抛出所有重复的异常,只保留原始字典。
  2. 忽略重复并继续。

有支持这两种用例的论据。 为此,您是否在签名中添加了AddRange标志?

AddRange标志(设置为true时)也会提供显着的加速,因为底层实现会绕过代码进行重复检查。

所以现在,你有一个标志允许AddRange支持这两种情况,但是有一个无证的副作用(这是框架设计者非常难以避免的工作)。

摘要

由于在处理重复项时没有明确,一致和预期的行为,因此不能一起处理它们更容易,并且不提供开始的方法。

如果您发现自己不断需要合并字典,您当然可以编写自己的扩展方法来合并字典,这些字典的行为方式适用于您的应用程序。

Alan answered 2019-08-13T14:21:15Z
24 votes

我找到了另一个解决方案:

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;
        }
    }

}

玩得开心。

ADM-IT answered 2019-08-13T14:21:47Z
12 votes

我的猜测是,对于发生的事情,用户缺乏适当的输出。由于你不能在字典中重复键,你如何处理合并两个字典的情况? 当然你可以说:&#34;我不在乎&#34; 但这打破了返回false /抛出重复键异常的惯例。

Gal answered 2019-08-13T14:22:14Z
8 votes

如果有人像我一样遇到这个问题 - 它可以实现&#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()).在这种情况下,它只是从重复组中获取第一个元素,但如果需要,您可以在那里实现更复杂的逻辑。

Rafal Zajac answered 2019-08-13T14:22:49Z
6 votes

你可以做到这一点

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>>
Valamas answered 2019-08-13T14:23:18Z
1 votes

如果您正在处理新词典(并且您不会丢失现有行),则可以始终使用另一个对象列表中的ToDictionary()。

所以,在你的情况下,你会做这样的事情:

Dictionary<string, string> dic = new Dictionary<string, string>();
dic = SomeList.ToDictionary(x => x.Attribute1, x => x.Attribute2);
WEFX answered 2019-08-13T14:23:53Z
1 votes

随意使用这样的扩展方法:

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;
}
Franziee answered 2019-08-13T14:24:20Z
-1 votes

如果您知道自己没有重复密钥,可以执行以下操作:

dic = dic.Union(MethodThatReturnAnotherDic()).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

如果存在重复的键/值对,它将抛出异常。

我不知道为什么这不在框架中; 应该。 没有不确定性; 只是抛出一个例外。 在这个代码的情况下,它确实抛出异常。

toddmo answered 2019-08-13T14:25:06Z
translate from https://stackoverflow.com:/questions/6050633/why-doesnt-dictionary-have-addrange