Словари.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
класс для сравнения коллекций:
РЕДАКТИРОВАТЬ словарь реализует 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
.