比较C#中的两个词典

sbe*_*rli 10 c# linq collections comparison dictionary

我有两个字典,都具有相同的结构和顺序(一个应该是另一个的精确复制):Dictionary<int, ICustomInterface>我想检查它们是否相同使用SequenceEqual<>

首先,我将第一个字典转换为XML,然后将其读回以重新创建第二个字典.初步检查时,它们都是相同的.该ICustomeInterface对象的每个正确重写Equals方法.为了检查这一点,我迭代两个词典的元素并进行比较.他们都是平等的.

但是当我调用SequenceEqual:dictionary1.SequenceEqual(dictionary2);它返回false并且ICustomInterface对象的Equals方法永远不会被调用,它总是返回false.但是,如果我这样做:

for (i = 0; i < firstDictionary.Count; i++)
   firstDictionary[i].SequenceEqual(otherSub.ItemSequence[i]);
Run Code Online (Sandbox Code Playgroud)

一切都按预期工作,每行都返回true.那么,当我简单地在字典本身上调用SequnceEqual时会发生什么?

Jon*_*eet 17

"正在发生什么"是它KeyValuePair按顺序比较两个词典的条目.字典本质上是无序的 - 你不应该依赖于条目从它们中出来的顺序.如果您使用:

firstDictionary.OrderBy(pair => pair.Key)
               .SequenceEqual(secondDictionary.OrderBy(pair => pair.Key))
Run Code Online (Sandbox Code Playgroud)

我怀疑你会发现那些比赛.尽管比较它们是一种非常不愉快的方式:)

  • 你很快会获得比我声望点更多的徽章! (4认同)

Ano*_*ken 5

Jon Skeet 已经给出了很好的解释。

但是,如果您(或阅读此问题的其他人)想要的是一种比较字典的有效方法,这里有一个简单的基于 Linq 的扩展,它可以做到这一点:

/// <summary>
/// Compares two dictionaries for equality.
/// </summary>
/// <returns>
/// True if the dictionaries have equal contents or are both null, otherwise false.
/// </returns>
public static bool DictionaryEqual<TKey, TValue>(
    this IDictionary<TKey, TValue> dict1, IDictionary<TKey, TValue> dict2,
    IEqualityComparer<TValue> equalityComparer = null)
{
    if (dict1 == dict2)
        return true;

    if (dict1 == null | dict2 == null)
        return false;

    if (dict1.Count != dict2.Count)
        return false;

    if (equalityComparer == null)
        equalityComparer = EqualityComparer<TValue>.Default;

    return dict1.All(kvp =>
        {
            TValue value2;
            return dict2.TryGetValue(kvp.Key, out value2)
                && equalityComparer.Equals(kvp.Value, value2);
        });
}
Run Code Online (Sandbox Code Playgroud)

它可能看起来有点蓬松,但我想要良好的可读性(和空测试)。

因此,如果您想要的只是一个“单行”,并且您已经知道两个字典都是非空的并且该TValue类型正确地覆盖了 Equals 方法,那么您实际上只需要这么多(没有 null 检查 if TValueis a valuetype当然):

bool isEqual = dict1.Count == dict2.Count && dict1.All(kvp =>
    {
        TValue value2;
        return dict2.TryGetValue(kvp.Key, out value2)
            && (kvp.Value == null ? value2 == null : kvp.Value.Equals(value2));
    });
Run Code Online (Sandbox Code Playgroud)

如果您想要进行比较,其中字典不必具有相同类型的值,或者如果您更喜欢使用委托或 lambda 表达式而不是必须实现 IEqualityComparer,则此扩展将为您提供帮助:

/// <summary>
/// Compares two dictionaries for equality using a custom value equality function.
/// </summary>
/// <returns>
/// True if both dictionaries are null or both have the same set of keys and comparing
/// their respective values for each key using the <paramref name="valueEqualityFunc"/>
/// returns true, otherwise false.
/// </returns>
public static bool DictionaryEqual<TKey, TValue1, TValue2>(
    this IDictionary<TKey, TValue1> dict1, IDictionary<TKey, TValue2> dict2,
    Func<TValue1, TValue2, bool> valueEqualityFunc)
{
    if (valueEqualityFunc == null)
        throw new ArgumentNullException("valueEqualityFunc");

    if (dict1 == dict2)
        return true;

    if (dict1 == null | dict2 == null)
        return false;

    if (dict1.Count != dict2.Count)
        return false;

    return dict1.All(kvp =>
    {
        TValue2 value2;
        return dict2.TryGetValue(kvp.Key, out value2)
            && valueEqualityFunc(kvp.Value, value2);
    });
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它与以前几乎相同。

这是一个用法示例:

var d1 = new Dictionary<string, string>();
var d2 = new Dictionary<string, string>();

d1.Add("key1", "dog");
d2.Add("key1", "Dog");
d1.Add("key2", "CAT");
d2.Add("key2", "cat");

bool isEqual = DictionaryEqual(d1, d2,
    (s1, s2) => string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase));
Run Code Online (Sandbox Code Playgroud)

如果运行上面的代码isEqual就会变为true。


注意: 正如用户 LukeSchoen 在评论中指出的那样,如果正在测试的字典不使用相同的 EqualityComparer 来比较键,则此方法可能无法给出预期结果(请记住,您可以选择为中的键指定一个相等比较器)字典构造函数,例如使用不区分大小写的字符串键)

这是无法修复的,因为人们无法概括尝试将两个使用不同相等定义的字典等同起来的“预期”结果。

一般情况下唯一的解决方案是让调用者指定自己的 EqaualityComparer 来进行字典间键比较,类似于上面的代码如何让调用者指定如何比较值,从而使调用者有责任提供合理的定义他们的用例的平等性。