Словари .NET имеют одинаковые ключи и значения, но не равны - PullRequest
16 голосов
/ 08 февраля 2010

Этот тест не пройден:

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 ]

21 голосов
/ 08 февраля 2010

Словарь класса не переопределяет 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, вы можете увидеть, работает ли это просто? Возможно, вам придется использовать this overload для сравнения двух словарных статей.

EDIT Хмм 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 не доступен только для чтения, поэтому вы не слишком много скрываете, используя его вместо concreate Dictionary).

9 голосов
/ 18 июня 2012

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

Попробуйте это

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).     
7 голосов
/ 08 февраля 2010

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

Assert.AreEqual(dict, dictClone)

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

4 голосов
/ 08 февраля 2010

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

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;
}
1 голос
/ 08 февраля 2010

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

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

...