比较c#中相同数据的两个词典?

我有两个字典包含一个字符串键,然后是一个对象。 该对象包含五个字段。 是否有一种优雅的方法来确保两个字典首先包含相同的键,然后如果这是正确的,每个对象包含相同的五个字段?

这两个词典是否具有相同的内置哈希码或其他东西?

编辑,似乎不适用于以下代码:

Dictionary test1 = new Dictionary(); Dictionary test2 = new Dictionary(); MyClass i = new MyClass("", "", 1, 1, 1, 1); MyClass j = new MyClass("", "", 1, 1, 1, 1); test1.Add("1", i); test2.Add("1", j); bool equal = test1.OrderBy(r => r.Key).SequenceEqual(test2.OrderBy(r => r.Key)); class MyClass { private string a; private string b; private long? c; private decimal d; private decimal e; private decimal f; public MyClass(string aa, string bb, long? cc, decimal dd, decimal ee, decimal ff) { a= aa; b= bb; c= cc; d= dd; e= ee; f= ff; } 

这返回false?

首先,您必须在类中重写EqualsGetHashCode方法,否则将对引用而不是实际值执行比较。 (最后提供了覆盖EqualsGetHashCode的代码) ,之后您可以使用:

 var result = (dic1 == dic2) || //Reference comparison (if both points to same object) (dic1.Count == dic2.Count && !dic1.Except(dic2).Any()); 

由于返回Dictionary中的项目顺序是未定义的 ,因此您不能依赖Dictionary.SequenceEqual (没有OrderBy

你可以试试:

 Dictionary dic1 = new Dictionary(); Dictionary dic2 = new Dictionary(); dic1.Add("Key1", new { Name = "abc", Number = "123", Address = "def", Loc = "xyz" }); dic1.Add("Key2", new { Name = "DEF", Number = "123", Address = "def", Loc = "xyz" }); dic1.Add("Key3", new { Name = "GHI", Number = "123", Address = "def", Loc = "xyz" }); dic1.Add("Key4", new { Name = "JKL", Number = "123", Address = "def", Loc = "xyz" }); dic2.Add("Key1",new { Name = "abc",Number= "123", Address= "def", Loc="xyz"}); dic2.Add("Key2", new { Name = "DEF", Number = "123", Address = "def", Loc = "xyz" }); dic2.Add("Key3", new { Name = "GHI", Number = "123", Address = "def", Loc = "xyz" }); dic2.Add("Key4", new { Name = "JKL", Number = "123", Address = "def", Loc = "xyz" }); bool result = dic1.SequenceEqual(dic2); //Do not use that 

大多数情况下,上面的内容将返回true ,但由于Dictionary无序性,人们不能真正依赖它。

由于SequenceEqual也会比较顺序,因此依赖SequenceEqual可能是错误的 。 您必须使用OrderBy来订购两个词典,然后使用SequenceEqual如:

 bool result2 = dic1.OrderBy(r=>r.Key).SequenceEqual(dic2.OrderBy(r=>r.Key)); 

但这将涉及多次迭代,一次用于排序,另一次用于使用SequenceEqual比较每个元素。

重写EqualsGetHashCode代码

 private class MyClass { private string a; private string b; private long? c; private decimal d; private decimal e; private decimal f; public MyClass(string aa, string bb, long? cc, decimal dd, decimal ee, decimal ff) { a = aa; b = bb; c = cc; d = dd; e = ee; f = ff; } protected bool Equals(MyClass other) { return string.Equals(a, other.a) && string.Equals(b, other.b) && c == other.c && e == other.e && d == other.d && f == other.f; } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; return Equals((MyClass)obj); } public override int GetHashCode() { unchecked { var hashCode = (a != null ? a.GetHashCode() : 0); hashCode = (hashCode * 397) ^ (b != null ? b.GetHashCode() : 0); hashCode = (hashCode * 397) ^ c.GetHashCode(); hashCode = (hashCode * 397) ^ e.GetHashCode(); hashCode = (hashCode * 397) ^ d.GetHashCode(); hashCode = (hashCode * 397) ^ f.GetHashCode(); return hashCode; } } } 

您还可以看到: 正确的方法来重写Equals()和GetHashCode()

您可以使用

 bool dictionariesEqual = dic1.Keys.Count == dic2.Keys.Count && dic1.Keys.All(k => dic2.ContainsKey(k) && object.Equals(dic2[k], dic1[k])); 

Dictionary的内置Equals函数仅检查引用相等性,请参阅SO上的此问题 。 Hashcodes不能可靠地告诉您两个对象是否相等; 总是存在哈希冲突的可能性。 永远不要使用哈希码作为相等测试!

我会手工完成:比较两个字典的条目数,迭代一个字典的键值对,检查另一个字典中是否存在密钥,并比较两个字典中的相应对象。 编辑:看罗林的答案:)

这里有几个答案我认为非常接近,但我认为应该添加几个额外的点,所以我将它们添加为另一个可能的答案。

首先,我会避免使用SequenceEquals方法。 它是Enumerable的扩展方法,并且隐式地要求两个集合具有相同的顺序。 字典不是有序集合,因此使用SequenceEquals意味着您必须不必要地遍历两个字典以创建您不需要的排序/有序中间集合,然后迭代这些集合以比较它们的相等性。 这似乎是非常低效和滥用LINQ,所有这些都是为了简化并编写一个单行解决方案。 如果OP的“优雅”的想法很简洁,我想这会成功,但这看起来很浪费。

另一方面,如果OP的“优雅”概念是有效的,那么您可能需要编写更多代码。 首先,您应该覆盖类的Equals方法或在类中实现IEquatable(例如,请参见此处 )。 这将允许您比较字典中的值。 然后,您可能想要为您的字典实现像IEqualityComparer这样的接口。

然后,两个词典的比较将如下所示。 它只是一个快速而肮脏的“背面餐巾纸”示例,所以它不是最佳方法的一个例子,但它的目的是说明一种方法,只需要在字典上进行多次迭代,并尽快退出找到了不平等。

首先是所需的代码:

 public class Foo { //members here... public override bool Equals(object obj) { //implementation here } //You should probably also override GetHashCode to be thorough, //but that's an implementation detail... } //This method could stand on its own or you could change it to make it //part of the implementation of one of the comparison interfaces... bool DictionariesEqual(Dictionary x, Dictionary y) { //If we're comparing the same object, it's obviously equal to itself. if(x == y) { return true; } //Make sure that we don't have null objects because those are //definitely not equal. if (x == null || y == null) { return false; } //Stop processing if at any point the dictionaries aren't equal. bool result = false; //Make sure that the dictionaries have the same count. result = x.Count == y.Count; //If we passed that check, keep going. if(result) { foreach(KeyValuePair xKvp in x) { //If we don't have a key from one in the other, even though //the counts are the same, the dictionaries aren't equal so //we can fail out. Foo yValue; if(!y.TryGetValue(xKvp.Key, out yValue)) { result = false; break; } else { //Use the override of the Equals method for your object //to see if the value from y is equal to the value from //x. result = xKvp.Value.Equals(yValue); if(!result) { //If they're not equal we can just quit out. break; } } } } return result; } 

然后我们就像这样使用它:

 Dictionary dict1 = new Dictionary(); Dictionary dict2 = new Dictionary(); //Fill the dictionaries here... //Compare the dictionaries bool areDictsEqual = DictionariesEqual(dict1, dict2); 

所以,它不是最简洁的代码,但它也不是必要的迭代。 在我看来,这更优雅。

在这种情况下,您可以使用SequenceEquals() – Method,如下所示:

  Dictionary d1 = new Dictionary(); d1.Add("first", new { Name = "TestName", Age = 12, ID = 001 }); Dictionary d2 = new Dictionary(); d2.Add("first", new { Name = "TestName", Age = 12, ID = 001 }); Console.WriteLine(d1.SequenceEqual(d2)); //outputs True 

注意:为简单起见,我使用隐式类来填充字典。 代码对任何对象的工作方式都相同。 两个字典的哈希码不相等,可以通过执行以下操作轻松validation:

  Console.WriteLine(d1.GetHashCode() + " " + d2.GetHashCode()); //outputs different hashcodes