如何按值比较两个字典,即使键或值是引用类型?
How to compare two dictionaries by value, even if the key or value are reference types?
我想比较两个 Dictionary<SomeClass, List<AnotherClass>>
。无论 KeyValuePair
的顺序如何,都应比较字典。在评论中,建议对值进行排序然后使用 SequenceEquals
,但我不确定如何 Sort
字典(另外,即使对 lists
进行排序也有帮助,这是不可能的,因为据我了解,因为不能保证列表的通用类型是 IComparable
).
当我尝试使用 Equals
方法时,我总是得到 false
,因为它会检查 List
是否引用相等。我想让它检查 List
s 的值是否相等。如何实现?
例如,假设我有以下词典:
var dictionary1 = new Dictionary<Day, List<WorkSession>>
{
{ Day.Tuesday, new List<WorkSession>() { new WorkSession("22:00", "00:00") } },
{ Day.Monday, new List<WorkSession>() { new WorkSession("20:00", "00:00") } },
{ Day.Sunday, new List<WorkSession>() { new WorkSession("10:00", "00:00") } }
};
var dictionary2 = new Dictionary<Day, List<WorkSession>>
{
{ Day.Sunday, new List<WorkSession>() { new WorkSession("10:00", "00:00") } },
{ Day.Monday, new List<WorkSession>() { new WorkSession("20:00", "00:00") } },
{ Day.Tuesday, new List<WorkSession>() { new WorkSession("22:00", "00:00") } }
};
工作会议:
class WorkSession : IEquatable<WorkSession>
{
public string Entrance { get; private set; }
public string Exit { get; private set; }
public WorkSession(string entrance, string exit)
{
Entrance = entrance;
Exit = exit;
}
public override bool Equals(object obj)
{
return Equals(obj as WorkSession);
}
public bool Equals(WorkSession other)
{
return other != null &&
Entrance == other.Entrance &&
Exit == other.Exit;
}
public override int GetHashCode()
{
var hashCode = 1257807568;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Entrance);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Exit);
return hashCode;
}
public static bool operator ==(WorkSession session1, WorkSession session2)
{
return EqualityComparer<WorkSession>.Default.Equals(session1, session2);
}
public static bool operator !=(WorkSession session1, WorkSession session2)
{
return !(session1 == session2);
}
}
我要两个比较这些字典,结果应该是True
。我怎样才能做到这一点?
这里有一些粗略的内容可以帮助您入门。您需要考虑一些极端情况并相应地调整代码。
class Program
{
static void Main(string[] args)
{
var x = new Dictionary<SomeClass, List<AnotherClass>>();
var y = new Dictionary<SomeClass, List<AnotherClass>>();
x.Add(new SomeClass { SomeNumericProperty = 1 }, new List<AnotherClass> { new AnotherClass { SomeStringProperty = "1" } });
y.Add(new SomeClass { SomeNumericProperty = 1 }, new List<AnotherClass> { new AnotherClass { SomeStringProperty = "1" } });
var w = new MyCustomComparer();
var z = w.Equals(x, y);
}
}
public class MyCustomComparer : IEqualityComparer<Dictionary<SomeClass, List<AnotherClass>>>
{
public bool Equals(Dictionary<SomeClass, List<AnotherClass>> x, Dictionary<SomeClass, List<AnotherClass>> y)
{
var keysAreEqual = x.Keys.OrderBy(o => o.GetHashCode()).SequenceEqual(y.Keys.OrderBy(o => o.GetHashCode()));
var valuesAreEqual = x.SelectMany(o => o.Value).OrderBy(o => o.GetHashCode()).SequenceEqual(y.SelectMany(o => o.Value).OrderBy(o => o.GetHashCode()));
return keysAreEqual && valuesAreEqual;
}
public int GetHashCode(Dictionary<SomeClass, List<AnotherClass>> obj)
{
throw new NotImplementedException();
}
}
public class AnotherClass
{
protected bool Equals(AnotherClass other)
{
return string.Equals(SomeStringProperty, other.SomeStringProperty);
}
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((AnotherClass)obj);
}
public override int GetHashCode()
{
int hash = 13;
hash = (hash * 7) + SomeStringProperty.GetHashCode();
return hash;
}
public string SomeStringProperty { get; set; }
}
public class SomeClass
{
public int SomeNumericProperty { get; set; }
protected bool Equals(SomeClass other)
{
return SomeNumericProperty == other.SomeNumericProperty;
}
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((SomeClass)obj);
}
public override int GetHashCode()
{
int hash = 13;
hash = (hash * 7) + SomeNumericProperty.GetHashCode();
return hash;
}
}
下面是一个可能的解决方案。与其他一些答案相比,平等检查略有收紧。例如,添加了一些 null
检查,并以不同的方式检查值(基本上不仅检查 Values
是否相同,而且对于给定的 它们是否相同键).
此外,在比较列表时,数据按 WorkSession
的所有属性排序 - 以防两个不同的 WorkSession
值具有相同的哈希码。 更好的长期解决方案是 WorkSession
实施 IComparable
。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace MattConsole
{
class Program
{
static void Main(string[] args)
{
var x = new Dictionary<Day, List<WorkSession>>
{
{ Day.Tuesday, new List<WorkSession>() { new WorkSession("22:00", "00:00") } },
{ Day.Monday, new List<WorkSession>() { new WorkSession("20:00", "00:00") } },
{ Day.Sunday, new List<WorkSession>() { new WorkSession("10:00", "00:00") } }
};
var y = new Dictionary<Day, List<WorkSession>>
{
{ Day.Sunday, new List<WorkSession>() { new WorkSession("10:00", "00:00") } },
{ Day.Monday, new List<WorkSession>() { new WorkSession("20:00", "00:00") } },
{ Day.Tuesday, new List<WorkSession>() { new WorkSession("22:00", "00:00") } }
};
var w = new MyCustomComparer();
var shouldBeTrue = w.Equals(x, y);
Console.WriteLine(shouldBeTrue);
x[Day.Wednesday] = new List<WorkSession>() { new WorkSession("10:00", "00:00") };
x[Day.Thursday] = new List<WorkSession>() { new WorkSession("10:01", "00:01") };
y[Day.Thursday] = new List<WorkSession>() { new WorkSession("10:00", "00:00") };
y[Day.Wednesday] = new List<WorkSession>() { new WorkSession("10:01", "00:01") };
var shouldBeFalse = w.Equals(x, y);
Console.WriteLine(shouldBeFalse);
Console.ReadLine();
}
}
public class MyCustomComparer : IEqualityComparer<Dictionary<Day, List<WorkSession>>>
{
public bool Equals(Dictionary<Day, List<WorkSession>> x, Dictionary<Day, List<WorkSession>> y)
{
if (ReferenceEquals(x, null))
return ReferenceEquals(y, null);
if (ReferenceEquals(y, null))
return false;
if (x.Count != y.Count)
return false;
if (!x.Keys.OrderBy(z => z).SequenceEqual(y.Keys.OrderBy(z => z)))
return false;
foreach (var kvp in x)
{
List<WorkSession> matching;
if (y.TryGetValue(kvp.Key, out matching))
{
if (ReferenceEquals(matching, null))
return ReferenceEquals(kvp.Value, null);
if (ReferenceEquals(kvp.Value, null))
return false;
// ordering by hash code is not strictly necessary
if (
!matching.OrderBy(z => z.GetHashCode())
.ThenBy(z => z.Entrance).ThenBy(z => z.Exit)
.SequenceEqual(
kvp.Value.OrderBy(z => z.GetHashCode())
.ThenBy(z => z.Entrance).ThenBy(z => z.Exit)))
return false;
}
else
return false;
}
return true;
}
public int GetHashCode(Dictionary<Day, List<WorkSession>> obj)
{
throw new NotImplementedException();
}
}
public class WorkSession : IEquatable<WorkSession>
{
public string Entrance { get; private set; }
public string Exit { get; private set; }
public WorkSession(string entrance, string exit)
{
Entrance = entrance;
Exit = exit;
}
public override bool Equals(object obj)
{
return Equals(obj as WorkSession);
}
public bool Equals(WorkSession other)
{
return other != null &&
Entrance == other.Entrance &&
Exit == other.Exit;
}
public override int GetHashCode()
{
var hashCode = 1257807568;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Entrance);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Exit);
return hashCode;
}
public static bool operator ==(WorkSession session1, WorkSession session2)
{
return EqualityComparer<WorkSession>.Default.Equals(session1, session2);
}
public static bool operator !=(WorkSession session1, WorkSession session2)
{
return !(session1 == session2);
}
}
}
我想比较两个 Dictionary<SomeClass, List<AnotherClass>>
。无论 KeyValuePair
的顺序如何,都应比较字典。在评论中,建议对值进行排序然后使用 SequenceEquals
,但我不确定如何 Sort
字典(另外,即使对 lists
进行排序也有帮助,这是不可能的,因为据我了解,因为不能保证列表的通用类型是 IComparable
).
当我尝试使用 Equals
方法时,我总是得到 false
,因为它会检查 List
是否引用相等。我想让它检查 List
s 的值是否相等。如何实现?
例如,假设我有以下词典:
var dictionary1 = new Dictionary<Day, List<WorkSession>>
{
{ Day.Tuesday, new List<WorkSession>() { new WorkSession("22:00", "00:00") } },
{ Day.Monday, new List<WorkSession>() { new WorkSession("20:00", "00:00") } },
{ Day.Sunday, new List<WorkSession>() { new WorkSession("10:00", "00:00") } }
};
var dictionary2 = new Dictionary<Day, List<WorkSession>>
{
{ Day.Sunday, new List<WorkSession>() { new WorkSession("10:00", "00:00") } },
{ Day.Monday, new List<WorkSession>() { new WorkSession("20:00", "00:00") } },
{ Day.Tuesday, new List<WorkSession>() { new WorkSession("22:00", "00:00") } }
};
工作会议:
class WorkSession : IEquatable<WorkSession>
{
public string Entrance { get; private set; }
public string Exit { get; private set; }
public WorkSession(string entrance, string exit)
{
Entrance = entrance;
Exit = exit;
}
public override bool Equals(object obj)
{
return Equals(obj as WorkSession);
}
public bool Equals(WorkSession other)
{
return other != null &&
Entrance == other.Entrance &&
Exit == other.Exit;
}
public override int GetHashCode()
{
var hashCode = 1257807568;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Entrance);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Exit);
return hashCode;
}
public static bool operator ==(WorkSession session1, WorkSession session2)
{
return EqualityComparer<WorkSession>.Default.Equals(session1, session2);
}
public static bool operator !=(WorkSession session1, WorkSession session2)
{
return !(session1 == session2);
}
}
我要两个比较这些字典,结果应该是True
。我怎样才能做到这一点?
这里有一些粗略的内容可以帮助您入门。您需要考虑一些极端情况并相应地调整代码。
class Program
{
static void Main(string[] args)
{
var x = new Dictionary<SomeClass, List<AnotherClass>>();
var y = new Dictionary<SomeClass, List<AnotherClass>>();
x.Add(new SomeClass { SomeNumericProperty = 1 }, new List<AnotherClass> { new AnotherClass { SomeStringProperty = "1" } });
y.Add(new SomeClass { SomeNumericProperty = 1 }, new List<AnotherClass> { new AnotherClass { SomeStringProperty = "1" } });
var w = new MyCustomComparer();
var z = w.Equals(x, y);
}
}
public class MyCustomComparer : IEqualityComparer<Dictionary<SomeClass, List<AnotherClass>>>
{
public bool Equals(Dictionary<SomeClass, List<AnotherClass>> x, Dictionary<SomeClass, List<AnotherClass>> y)
{
var keysAreEqual = x.Keys.OrderBy(o => o.GetHashCode()).SequenceEqual(y.Keys.OrderBy(o => o.GetHashCode()));
var valuesAreEqual = x.SelectMany(o => o.Value).OrderBy(o => o.GetHashCode()).SequenceEqual(y.SelectMany(o => o.Value).OrderBy(o => o.GetHashCode()));
return keysAreEqual && valuesAreEqual;
}
public int GetHashCode(Dictionary<SomeClass, List<AnotherClass>> obj)
{
throw new NotImplementedException();
}
}
public class AnotherClass
{
protected bool Equals(AnotherClass other)
{
return string.Equals(SomeStringProperty, other.SomeStringProperty);
}
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((AnotherClass)obj);
}
public override int GetHashCode()
{
int hash = 13;
hash = (hash * 7) + SomeStringProperty.GetHashCode();
return hash;
}
public string SomeStringProperty { get; set; }
}
public class SomeClass
{
public int SomeNumericProperty { get; set; }
protected bool Equals(SomeClass other)
{
return SomeNumericProperty == other.SomeNumericProperty;
}
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((SomeClass)obj);
}
public override int GetHashCode()
{
int hash = 13;
hash = (hash * 7) + SomeNumericProperty.GetHashCode();
return hash;
}
}
下面是一个可能的解决方案。与其他一些答案相比,平等检查略有收紧。例如,添加了一些 null
检查,并以不同的方式检查值(基本上不仅检查 Values
是否相同,而且对于给定的 它们是否相同键).
此外,在比较列表时,数据按 WorkSession
的所有属性排序 - 以防两个不同的 WorkSession
值具有相同的哈希码。 更好的长期解决方案是 WorkSession
实施 IComparable
。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace MattConsole
{
class Program
{
static void Main(string[] args)
{
var x = new Dictionary<Day, List<WorkSession>>
{
{ Day.Tuesday, new List<WorkSession>() { new WorkSession("22:00", "00:00") } },
{ Day.Monday, new List<WorkSession>() { new WorkSession("20:00", "00:00") } },
{ Day.Sunday, new List<WorkSession>() { new WorkSession("10:00", "00:00") } }
};
var y = new Dictionary<Day, List<WorkSession>>
{
{ Day.Sunday, new List<WorkSession>() { new WorkSession("10:00", "00:00") } },
{ Day.Monday, new List<WorkSession>() { new WorkSession("20:00", "00:00") } },
{ Day.Tuesday, new List<WorkSession>() { new WorkSession("22:00", "00:00") } }
};
var w = new MyCustomComparer();
var shouldBeTrue = w.Equals(x, y);
Console.WriteLine(shouldBeTrue);
x[Day.Wednesday] = new List<WorkSession>() { new WorkSession("10:00", "00:00") };
x[Day.Thursday] = new List<WorkSession>() { new WorkSession("10:01", "00:01") };
y[Day.Thursday] = new List<WorkSession>() { new WorkSession("10:00", "00:00") };
y[Day.Wednesday] = new List<WorkSession>() { new WorkSession("10:01", "00:01") };
var shouldBeFalse = w.Equals(x, y);
Console.WriteLine(shouldBeFalse);
Console.ReadLine();
}
}
public class MyCustomComparer : IEqualityComparer<Dictionary<Day, List<WorkSession>>>
{
public bool Equals(Dictionary<Day, List<WorkSession>> x, Dictionary<Day, List<WorkSession>> y)
{
if (ReferenceEquals(x, null))
return ReferenceEquals(y, null);
if (ReferenceEquals(y, null))
return false;
if (x.Count != y.Count)
return false;
if (!x.Keys.OrderBy(z => z).SequenceEqual(y.Keys.OrderBy(z => z)))
return false;
foreach (var kvp in x)
{
List<WorkSession> matching;
if (y.TryGetValue(kvp.Key, out matching))
{
if (ReferenceEquals(matching, null))
return ReferenceEquals(kvp.Value, null);
if (ReferenceEquals(kvp.Value, null))
return false;
// ordering by hash code is not strictly necessary
if (
!matching.OrderBy(z => z.GetHashCode())
.ThenBy(z => z.Entrance).ThenBy(z => z.Exit)
.SequenceEqual(
kvp.Value.OrderBy(z => z.GetHashCode())
.ThenBy(z => z.Entrance).ThenBy(z => z.Exit)))
return false;
}
else
return false;
}
return true;
}
public int GetHashCode(Dictionary<Day, List<WorkSession>> obj)
{
throw new NotImplementedException();
}
}
public class WorkSession : IEquatable<WorkSession>
{
public string Entrance { get; private set; }
public string Exit { get; private set; }
public WorkSession(string entrance, string exit)
{
Entrance = entrance;
Exit = exit;
}
public override bool Equals(object obj)
{
return Equals(obj as WorkSession);
}
public bool Equals(WorkSession other)
{
return other != null &&
Entrance == other.Entrance &&
Exit == other.Exit;
}
public override int GetHashCode()
{
var hashCode = 1257807568;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Entrance);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Exit);
return hashCode;
}
public static bool operator ==(WorkSession session1, WorkSession session2)
{
return EqualityComparer<WorkSession>.Default.Equals(session1, session2);
}
public static bool operator !=(WorkSession session1, WorkSession session2)
{
return !(session1 == session2);
}
}
}