为什么Dictionary没有AddRange?

Cus*_*dio 104 .net c# dictionary

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

Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
Run Code Online (Sandbox Code Playgroud)

Ala*_*lan 67

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

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

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

AddRange不存在因为范围对关联容器没有任何意义,因为数据范围允许重复条目.例如,如果你有一个IEnumerable<KeyValuePair<K,T>>集合不防止重复条目.

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

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

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

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

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

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

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

然后选择归结为:

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

有支持这两种用例的论据.为此,您是否IgnoreDuplicates在签名上添加了一个标志?

IgnoreDuplicates标志(当设置为true)也将提供一个显著加快,作为底层实现将绕过重复检查的代码.

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

摘要

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

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

  • 完全错误,字典应该有一个AddRange(IEnumerable <KeyValuePair <K,T >>值) (31认同)
  • 如果它可以有Add,它也应该能够添加多个.尝试添加具有重复键的项时的行为应与添加单个重复键时的行为相同. (15认同)
  • `AddMultiple`与`AddRange`不同,不管它的实现是不是很糟糕:你用一个*all*重复键的数组抛出异常吗?或者你在遇到的第一个重复键上抛出异常?如果抛出异常,字典的状态应该是什么?原始,或所有成功的钥匙? (4认同)
  • @ doug65536因为作为API使用者,您现在可以决定要对每个"添加"执行什么操作 - 将每个"添加"包装在`try ... catch`中并以这种方式捕获重复项; 或者使用索引器并用后面的值覆盖第一个值; 或者在尝试"添加"之前使用"ContainsKey"进行抢先检查,从而保留原始值.如果框架有一个`AddRange`或`AddMultiple`方法,那么通过异常来传达已发生事件的唯一简单方法就是处理和恢复并不复杂. (4认同)
  • 好的,现在我必须手动迭代我的枚举并单独添加它们,并提供有关您提到的重复项的所有注意事项.如何从框架中省略它来解决任何问题? (3认同)

ADM*_*-IT 27

我发现了另一种解决方案:

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);
}
Run Code Online (Sandbox Code Playgroud)

...

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

}
Run Code Online (Sandbox Code Playgroud)

玩得开心.

  • 好的,`ToList()` 不是一个好的解决方案,所以我更改了代码。您可以使用`try { mainDic.AddRange(addDic); } catch { do something }` 如果你不确定第三种方法。第二种方法效果很好。 (2认同)

Gal*_*Gal 12

我的猜测是,对于发生的事情,用户缺乏适当的输出.由于你不能在字典中重复键,你将如何处理合并两个字典,其中一些键相交?当然你可以说:"我不在乎",但这违反了返回false /抛出重复键异常的惯例.

  • 当你调用"添加"时,你与键盘碰撞的位置有什么不同,除此之外它可能不止一次发生.它会抛出"Add"所做的相同的`ArgumentException`,当然? (5认同)

Raf*_*jac 11

如果有人像我一样遇到这个问题 - 可以通过使用IEnumerable扩展方法来实现"AddRange":

var combined =
    dict1.Union(dict2)
        .GroupBy(kvp => kvp.Key)
        .Select(grp => grp.First())
        .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Run Code Online (Sandbox Code Playgroud)

组合字典时的主要技巧是处理重复键.在上面的代码中它是部分.Select(grp => grp.First()).在这种情况下,它只需要从重复组中获取第一个元素,但如果需要,您可以在那里实现更复杂的逻辑.


Val*_*mas 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(.., ..);
}
Run Code Online (Sandbox Code Playgroud)

或者使用List for addrange和/或使用上面的模式.

List<KeyValuePair<string, string>>
Run Code Online (Sandbox Code Playgroud)