我有两个字典,包含一个字符串键,然后是一个对象。该对象包含五个字段。是否有一种优雅的方法可以确保两个字典首先包含相同的键,然后如果这是正确的,每个对象包含相同的五个字段?
这两个词典是否具有相同的内置哈希码或其他东西?
编辑,似乎不适用于以下代码:
Dictionary<string, MyClass> test1 = new Dictionary<string, MyClass>();
Dictionary<string, MyClass> test2 = new Dictionary<string, MyClass>();
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?
答案 0 :(得分:19)
您可以使用
bool dictionariesEqual =
dic1.Keys.Count == dic2.Keys.Count &&
dic1.Keys.All(k => dic2.ContainsKey(k) && object.Equals(dic2[k], dic1[k]));
答案 1 :(得分:18)
首先,您必须覆盖类中的Equals
和GetHashCode
方法,否则将对引用而不是实际值执行比较。 (最后提供了覆盖Equals
和GetHashCode
的代码),之后您可以使用:
var result = (dic1 == dic2) || //Reference comparison (if both points to same object)
(dic1.Count == dic2.Count && !dic1.Except(dic2).Any());
由于返回词典中的项目的顺序未定义,因此您无法依赖Dictionary.SequenceEqual (没有OrderBy
)。
您可以尝试:
Dictionary<string, object> dic1 = new Dictionary<string, object>();
Dictionary<string, object> dic2 = new Dictionary<string, object>();
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
比较每个元素。
覆盖Equals
和GetHashCode
的代码
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;
}
}
}
答案 2 :(得分:1)
在这种情况下,您可以使用SequenceEquals() - Method,如下所示:
Dictionary<string, object> d1 = new Dictionary<string, object>();
d1.Add("first", new { Name = "TestName", Age = 12, ID = 001 });
Dictionary<string, object> d2 = new Dictionary<string, object>();
d2.Add("first", new { Name = "TestName", Age = 12, ID = 001 });
Console.WriteLine(d1.SequenceEqual(d2)); //outputs True
注意:为简单起见,我使用隐式类来填充词典。代码将以任何对象的相同方式工作。两个字典的哈希码都不相等,可以通过执行以下操作轻松验证:
Console.WriteLine(d1.GetHashCode() + " " + d2.GetHashCode()); //outputs different hashcodes
答案 3 :(得分:1)
Equals
的内置Dictionary<T>
函数仅检查引用相等性,请参阅this question on SO。 Hashcodes不能可靠地告诉您两个对象是否相等;总是存在哈希冲突的可能性。永远不要使用哈希码作为相等测试!
我会手工完成:比较两个字典的条目数,迭代一个字典的键值对,并检查另一个字典中是否存在密钥,并比较两个字典中的相应对象。 编辑:请参阅罗林的回答:)
答案 4 :(得分:1)
这里有几个答案,我认为非常接近,但我认为应该添加几个额外的点,所以我将它们作为另一个可能的答案添加。
首先,我会避免使用SequenceEquals方法。它是Enumerable的扩展方法,并且隐式地要求两个集合具有相同的顺序。字典不是有序集合,因此使用SequenceEquals意味着您必须不必要地遍历两个字典以创建您不需要的排序/有序中间集合,然后迭代这些集合以比较它们的相等性。这似乎是非常低效和滥用LINQ,所有这些都是为了简化并编写一个单行解决方案。如果OP的“优雅”的想法很简洁,我想这会解决问题,但这似乎很浪费。
另一方面,如果OP的“优雅”理念是有效的,那么你可能需要编写更多的代码。首先,您应该覆盖类的Equals方法或在类中实现IEquatable(例如,请参阅here)。这将允许您比较字典中的值。然后,您可能想要为您的字典实现像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<string, Foo> x, Dictionary<string, Foo> 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<string, Foo> 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<string, Foo> dict1 = new Dictionary<string, Foo>();
Dictionary<string, Foo> dict2 = new Dictionary<string, Foo>();
//Fill the dictionaries here...
//Compare the dictionaries
bool areDictsEqual = DictionariesEqual(dict1, dict2);
所以,它不是最简洁的代码,但它也不是必要的迭代。在我看来,这更优雅。