.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(), не пытаясь проверить ссылочное равенство.

Ответы

Ответ 1

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

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

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

Увидев, что вы выполняете модульное тестирование, ваш класс Assert должен предоставить тестовый метод для тестирования, если две коллекции одинаковы.

Структура тестирования Microsoft Unit предоставляет класс CollectionAssert для сравнения коллекций:

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

EDIT Словарь реализует интерфейс ICollection, вы можете видеть, работает ли это только так? Возможно, вам придется использовать эту перегрузку для сравнения двух слов в словаре.

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

Ответ 2

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

Попробуйте

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

Ответ 3

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

Assert.AreEqual(dict, dictClone)

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

Ответ 4

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

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

Ответ 5

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

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