Словари.NET имеют одинаковые ключи и значения, но не равны

Этот тест не проходит:

using Microsoft.VisualStudio.TestTools.UnitTesting;        

[TestMethod()]
        public void dictEqualTest() {
            IDictionary<string, int> dict = new Dictionary<string, int>();
            IDictionary<string, int> dictClone = new Dictionary<string, int>();

        for (int x = 0; x < 3; x++) {
            dict[x.ToString()] = x;
            dictClone[x.ToString()] = x;
        }

        Assert.AreEqual(dict, dictClone); // fails here
        Assert.IsTrue(dict.Equals(dictClone)); // and here, if the first is commented out
        Assert.AreSame(dict, dictClone); // also fails
    }

Я что-то неправильно понимаю о том, как Dictionary работает?

Я ищу Java-эквивалент .equals(), не пытаясь проверить ссылочное равенство.

5 ответов

Решение

Класс словаря не переопределяет Object.Equals метод, как видно из документа MSDN:

http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx

Определяет, равен ли указанный объект текущему объекту.

Видя, что вы делаете юнит-тестирование, ваш AssertКласс должен предоставить метод тестирования для тестирования, если две коллекции совпадают.

Структура модульного тестирования Microsoft обеспечивает CollectionAssert класс для сравнения коллекций:

http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.collectionassert_members%28VS.80%29.aspx

РЕДАКТИРОВАТЬ словарь реализует ICollection интерфейс, вы можете увидеть, если это просто работает? Возможно, вам придется использовать эту перегрузку для сравнения двух словарных статей.

РЕДАКТИРОВАТЬ Хмм IDictionary не реализует ICollection, что немного боли. Это однако работает (хотя взломать):

IDictionary<string, int> dict = new Dictionary<string, int>();
IDictionary<string, int> dictClone = new Dictionary<string, int>();

for(int x = 0; x < 3; x++) {
    dict[x.ToString()] = x;
    dictClone[x.ToString()] = x;
}

CollectionAssert.AreEqual((System.Collections.ICollection)dict, (System.Collections.ICollection)dictClone);

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

Если вы особенно заинтересованы в том, как это можно исправить с точки зрения модульного тестирования:

Попробуй это

CollectionAssert.AreEquivalent(dict.ToList(), dictClone.ToList());

объяснение

В IDictionary есть методы расширения, такие как .ToList() - доступно в.Net 3.5 и выше, что преобразует словарь в коллекцию KeyValuePair, которую можно легко сравнить с CollectionAssert.AreEquivalent,

Они даже дадут достаточно полезные сообщения об ошибках! Пример использования:

IDictionary<string, string> d1 = new Dictionary<string, string> {
    { "a", "1"}, {"b", "2"}, {"c", "3"}};

IDictionary<string, string> d2 = new Dictionary<string, string> {
    {"b", "2"}, { "a", "1"}, {"c", "3"}}; // same key-values, different order

IDictionary<string, string> d3 = new Dictionary<string, string> {
    { "a", "1"}, {"d", "2"}, {"c", "3"}}; // key of the second element differs from d1

IDictionary<string, string> d4 = new Dictionary<string, string> {
    { "a", "1"}, {"b", "4"}, {"c", "3"}}; // value of the second element differs from d1

CollectionAssert.AreEquivalent(d1.ToList(), d2.ToList());
//CollectionAssert.AreEquivalent(d1.ToList(), d3.ToList()); // fails!
//CollectionAssert.AreEquivalent(d1.ToList(), d4.ToList()); // fails!

// if uncommented, the 2 tests above fail with error:
//   CollectionAssert.AreEquivalent failed. The expected collection contains 1
//   occurrence(s) of <[b, 2]>. The actual collection contains 0 occurrence(s).     

Проблема с этой строкой кода:

Assert.AreEqual(dict, dictClone)

Вы сравниваете ссылки на объекты, которые не равны.

Я использовал метод расширения, который проверяет две последовательности на одинаковые элементы

public static bool CheckForEquality<T>(this IEnumerable<T> source, IEnumerable<T> destination)
{
    if (source.Count() != destination.Count())
    {
        return false;
    }

    var dictionary = new Dictionary<T, int>();

    foreach (var value in source)
    {
        if (!dictionary.ContainsKey(value))
        {
            dictionary[value] = 1;
        }
        else
        {
            dictionary[value]++;
        }
    }

    foreach (var member in destination)
    {
        if (!dictionary.ContainsKey(member))
        {
            return false;
        }

        dictionary[member]--;
    }

    foreach (var kvp in dictionary)
    {
        if (kvp.Value != 0)
        {
            return false;
        }
    }

    return true;
}

Вы совершенно не понимаете, как работают ссылочные типы.

Dictionary не переопределяет object.Equals(), Таким образом, он использует равенство ссылок - в основном, если обе ссылки указывают на один и тот же экземпляр, они равны, в противном случае это не так.

Класс NUnit CollectionAssert имеет AreEquivalent метод, который принимает IEnumerableв качестве параметров, поэтому в этом случае это так же просто, как

CollectionAssert.AreEquivalent(dict, dictClone);

потому как Dictionary орудия IEnumerable.

Другие вопросы по тегам