Сравнение двух словарей в C # - PullRequest
9 голосов
/ 19 июля 2011

У меня есть два словаря, оба с одинаковой структурой и порядком (один должен быть точной копией другого): Dictionary<int, ICustomInterface>, и я хочу проверить, что они равны, используя SequenceEqual<>

Сначала я превращаю первый словарь в XML, а затем снова читаю его, чтобы воссоздать второй.При первоначальном осмотре они оба одинаковы.Каждый объект ICustomeInterface корректно переопределяет метод Equals.Чтобы проверить это, я перебираю элементы двух словарей и сравниваю их.Все они равны.

Но когда я вызываю SequenceEqual: dictionary1.SequenceEqual(dictionary2);, он возвращает false, и методы Equals объектов ICustomInterface никогда не вызывают, и он всегда возвращает false.Тем не менее, если я сделаю это:

for (i = 0; i < firstDictionary.Count; i++)
   firstDictionary[i].SequenceEqual(otherSub.ItemSequence[i]);

все работает как положено и возвращает true для каждой строки.Итак, что происходит, когда я просто вызываю SequnceEqual для самого словаря?

Ответы [ 2 ]

16 голосов
/ 19 июля 2011

«Что происходит» - это сравнение KeyValuePair записей для двух словарей по порядку. Словари по своей природе неупорядочены - вы не должны полагаться ни на что относительно порядка, в котором из них выходят записи. Если вы используете:

firstDictionary.OrderBy(pair => pair.Key)
               .SequenceEqual(secondDictionary.OrderBy(pair => pair.Key))

Я подозреваю, вы найдете это совпадение Это довольно неприятный способ сравнить их:)

1 голос
/ 22 февраля 2016

Джон Скит уже дал хорошее объяснение.

Однако, если все, что вам (или кому-либо еще, читающему этот вопрос), нужен эффективный метод сравнения словарей, это простое расширение на основе 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);
        });
}

Возможно, это выглядит немного пушистым, но я хотел хорошей читабельности (и нулевых тестов).

Так что, если все, что вы хотите, это "одна строка" и вы уже знаете, что оба словаря не равны NULL и что тип TValue корректно переопределяет метод Equals, тогда вам действительно нужно это очень много (без нулевых проверок, если TValue, конечно, тип значения):

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

Если вы хотите выполнить сравнение, когда словари не должны иметь одинаковый тип значения, или если вы предпочитаете использовать делегат или лямбда-выражение вместочтобы реализовать 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);
    });
}

Как вы можете видеть, это почти то же самое, что и раньше.

Вот пример СШАпример ge:

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

Если вы запустите приведенный выше код, isEqual станет истинным.

...