如何在两个Dictionary <string,list <string =“”>&gt;之间进行值比较对象</串,>

时间:2011-12-14 09:38:56

标签: c# unit-testing

我有以下数据结构:

Dictionary<string, List<string>>

如何进行比较以确保两个不同对象之间的值相等?

即:

    Dictionary<string, List<string>> expected = new Dictionary<string, List<string>>();
    expected.Add("CREDIT", new List<string> { "K   R   EH   D   IH   T" });
    expected.Add("CARD", new List<string> { "K   AA   R   D" });

    Dictionary<string, List<string>> actual;
    actual = target.GetTermDictionary();
    if (!Enumerable.SequenceEqual(expected, actual))
    {
        Assert.Fail();
    }

我不认为SequanceEqual在这里很好..

由于

3 个答案:

答案 0 :(得分:4)

快速结队和失败的第一条捷径:

if(ReferenceEqual(actual, expected))
  return true;
if(actual == null || expected == null || actual.Count != expected.Count)
  return false;

这也处理空值检查,因此我们不会抛出空引用异常。你可以跳过所有这个比较计数的条,如果你刚刚在你的例子中创建了它,但是如果你把它放在一个单独的方法中则应该保留它,以防万一。

我们不能只在两个词典上调用SequenceEqual,因为我们无法保证以相同的顺序返回键。我们可以使用其他类型的值:

return actual.OrderBy(kvp => kvp.Key).SequenceEqual(expected.OrderBy(kvp => kvp.Key));

但是这不起作用,因为两个序列相等的List<string>值不会被认为等于它将调用的DefaultEqualityComparer<List<string>>.Equals()方法。

如果我们在使用SequenceEqual时受到地狱限制,我们可以创建一个IEqualityComparer<KeyValuePair<string, List<string>>>,但是使用非Linq方法可能更简单,即使Linq通常更简单,更简洁(一旦找到通往这样做。因此:

List<string> expectedVal;
foreach(KeyValuePair<string, List<string> kvp in actual)
{
  if(!expected.TryGetValue(kvp.key, out expectedVal) || kvp.Value.Count != expectedVal.Count || !kvp.Value.SequenceEquals(expectedVal))
    return false;

}
return true;

变体可以处理不同的平等观点。例如,如果我们想要考虑不同订单中相同项目的两个列表,我们可以使用kvp.Value.OrderBy(x => x).SequenceEquals(expectedVal.OrderBy(x => x))

总之,这个地方在一起:

if(ReferenceEqual(actual, expected))
  return true;
if(actual == null || expected == null || actual.Count != expected.Count)
  return false;
List<string> expectedVal;
foreach(KeyValuePair<string, List<string> kvp in actual)
{
  if(!expected.TryGetValue(kvp.key, out expectedVal) || kvp.Value.Count != expectedVal.Count || !kvp.Value.SequenceEquals(expectedVal))
    return false;

}
return true;

编辑:只是为了好玩,使用SequenceEquals的方式:

internal class KvpSLSEq : IEqualityComparer<KeyValuePair<string, List<string>>>
{
  public bool Equals(KeyValuePair<string, List<string>> x, KeyValuePair<string, List<string>> y)
  {
    return x.Key == y.Key && x.Value.Count == y.Value.Count && x.Value.SequenceEquals(y.Value);
  }
  public int GetHashCode(KeyValuePair<string, List<string>> obj)
  {
    //you could just throw NotImplementedException unless you'll reuse this elsewhere.
    int hash = obj.Key.GetHashCode;
    foreach(string val in obj.Value)
       hash = hash * 31 + (val == null ? 0 : val.GetHashCode());
  }
}

这样做我们可以使用简洁:

actual.OrderBy(kvp => kvp.Key).SequenceEqual(expected.OrderBy(kvp => kvp.Key), new KvpSLSEq());

但如果KvpSLSEq也将在其他地方使用,那么它真的很简洁。

答案 1 :(得分:1)

我认为没有内置方法,但您可以比较每个词典条目中的列表值。像这样:

// Check actual doesn't contain excess keys
if (actual.Keys.Count != expected.Keys.Count)
{
    return false;
}

foreach(var key in expected.Keys)
{
    if (!actual.ContainsKey(key) || !actual[key].SequenceEqual(expected[key]))
    {
       return false;
    }
}

return true;

在这里查看:Comparing 2 Dictionary<string, string> Instances和此处:Is there a built-in method to compare collections in C#?

答案 2 :(得分:0)

如果您使用的是NUnit,可以使用CollectionAssert.AreEquivalent,请参阅此问题:NUnit: Dictionary Assert