I have a model class Class1
and I want to compare if two instances of Class1
are same (structural equality).
public class Class1 : IEquatable<Class1>
{
public string Id { get; set; }
public string Name { get; set; }
public IList<Class2> Class2s { get; set; }
public bool Equals(Class1 other)
{
return QuestName.Equals(other.QuestName)
&& Class2s.OrderBy(c => c.Id).SequenceEqual(other.Class2s.OrderBy(c => c.Id));
//Below method is very fast but not so accurate
//because 2 objects with the same hash code may or may not be equal
//return GetHashCode() == other.GetHashCode();
}
public override bool Equals(object obj)
{
return obj is Class1
&& this.Equals(obj as Class1);
}
public override int GetHashCode()
{
unchecked
{
int hash = 13;
hash = (hash * 7) + Name.GetHashCode();
foreach (var c2 in Class2s.OrderBy(c => c.Id))
{
hash = (hash * 7) + c2.GetHashCode();
}
return hash;
}
}
}
public class Class2 : IEquatable<Class2>
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Class3> Class3s { get; set; }
public bool Equals(Class2 other)
{
return Id == other.Id
&& Name.Equals(other.Name)
&& Class3s.OrderBy(c => c.Id).SequenceEqual(other.Class3s.OrderBy(c => c.Id));
}
public override bool Equals(object obj)
{
return obj is Class2
&& this.Equals(obj as Class2 );
}
public override int GetHashCode()
{
unchecked
{
int hash = 13;
hash = (hash * 7) + Id.GetHashCode();
hash = (hash * 7) + Name.GetHashCode();
foreach (var c3 in Class3s.OrderBy(c => c.Id))
{
hash = (hash * 7) + c3.GetHashCode();
}
return hash;
}
}
}
public class Class3 : IEquatable<Class3>
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Class4> Class4s { get; set; }
public bool Equals(Class3 other)
{
return Id == other.Id
&& Name.Equals(other.Name)
&& Class4s.OrderBy(c => c.Id).SequenceEqual(other.Class4s.OrderBy(c => c.Id));
}
public override bool Equals(object obj)
{
return obj is Class3
&& this.Equals(obj as Class3);
}
public override int GetHashCode()
{
unchecked
{
int hash = 13;
hash = (hash * 7) + Id.GetHashCode();
hash = (hash * 7) + Name.GetHashCode();
foreach (var c in Class4s.OrderBy(c => c.Id))
{
hash = (hash * 7) + c.GetHashCode();
}
return hash;
}
}
}
public class Class4 : IEquatable<Class4>
{
public int Id { get; set; }
public string Name { get; set; }
public bool Equals(Class4 other)
{
return Id.Equals(other.Id)
&& Name.Equals(other.Name);
}
public override bool Equals(object obj)
{
return obj is Class4
&& this.Equals(obj as Class4);
}
public override int GetHashCode()
{
unchecked
{
int hash = 13;
hash = (hash * 7) + Id.GetHashCode();
hash = (hash * 7) + Name.GetHashCode();
return hash;
}
}
}
I say that two Class1
objects are equal when:
1. they have the same Name
2. they have the same Class2
objects (their order does not matter)
Two Class2
objects are equal:
1. They have the same Id
2. They Have the same name
3. they have the same Class3
objects (their order does not matter)
Two Class3
objects are equal:
1. They have the same Id
2. They Have the same name
3. they have the same Class4
objects (their order does not matter)
Two Class4
objects are equal:
1. They ave the same Id
2. They Have the same name
I compare them using the Equals
method and measure the run time like this:
Class1 obj1 = GetFirstClass1Object();
Class1 obj2 = GetSecondClass1Object();
var startTime = DateTime.Now;
bool equals = obj1.Equals(obj2);
var elaspedTime = DateTime.Now.Substract(startTime)
the above solution works just fine but it's very slow.
I know that if we flatten obj1
and obj2
, they contain 3500 Class4
objects each and it takes around 12 seconds to compare obj1
and obj2
.
Is there any faster way to do this? Can I somehow make use of hashing to make this faster?
Also, the number of Class2
, Class3
and Class4
objects inside both obj1
and obj2
will always be same
Consider the following structure given the provided classes as examples. There was no sample data based on your example to test it against so you will have to test it with what you have.
public class Class1 : IEquatable<Class1> {
public int Id { get; set; }
public string Name { get; set; }
public IList<Class2> Class2s { get; set; }
public static bool operator ==(Class1 left, Class1 right) {
return Equals(left, right);
}
public static bool operator !=(Class1 left, Class1 right) {
return !(left == right);
}
public bool Equals(Class1 other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(this.ToString(), other.ToString());
}
public override bool Equals(object obj) {
return obj is Class1 other && this.Equals(other);
}
public override int GetHashCode() {
return ToString().GetHashCode();
}
public override string ToString() {
var cs = Class2s == null ? "" : string.Join("", Class2s.OrderBy(_ => _.Id).Select(_ => _.ToString()));
return string.Join("", Id, Name, cs);
}
}
public class Class2 : IEquatable<Class2> {
public int Id { get; set; }
public string Name { get; set; }
public IList<Class3> Class3s { get; set; }
public static bool operator ==(Class2 left, Class2 right) {
return Equals(left, right);
}
public static bool operator !=(Class2 left, Class2 right) {
return !(left == right);
}
public bool Equals(Class2 other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(this.ToString(), other.ToString());
}
public override bool Equals(object obj) {
return obj is Class2 other && this.Equals(other);
}
public override int GetHashCode() {
return ToString().GetHashCode();
}
public override string ToString() {
var cs = Class3s == null ? "" : string.Join("", Class3s.OrderBy(_ => _.Id).Select(_ => _.ToString()));
return string.Join("", Id, Name, cs);
}
}
public class Class3 : IEquatable<Class3> {
public int Id { get; set; }
public string Name { get; set; }
public IList<Class4> Class4s { get; set; }
public static bool operator ==(Class3 left, Class3 right) {
return Equals(left, right);
}
public static bool operator !=(Class3 left, Class3 right) {
return !(left == right);
}
public bool Equals(Class3 other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(this.ToString(), other.ToString());
}
public override bool Equals(object obj) {
return obj is Class3 other && this.Equals(other);
}
public override int GetHashCode() {
return ToString().GetHashCode();
}
public override string ToString() {
var cs = Class4s == null ? "" : string.Join("", Class4s.OrderBy(_ => _.Id).Select(_ => _.ToString()));
return string.Join("", Id, Name, cs);
}
}
public class Class4 : IEquatable<Class4> {
public int Id { get; set; }
public string Name { get; set; }
public static bool operator ==(Class4 left, Class4 right) {
return Equals(left, right);
}
public static bool operator !=(Class4 left, Class4 right) {
return !(left == right);
}
public bool Equals(Class4 other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(this.ToString(), other.ToString());
}
public override bool Equals(object obj) {
return obj is Class4 other && Equals(other);
}
public override int GetHashCode() {
return ToString().GetHashCode();
}
public override string ToString() {
return string.Format("{0}{1}", Id, Name);
}
}
The structure for all the objects are similar except for Class4
obviously, since it has no inner list.
Though just an example, a lot of the repeated code could be refactored into a common base class.