C# 优化复杂对象比较

C# 优化复杂对象比较,c#,list,performance,linq,comparison,C#,List,Performance,Linq,Comparison,我有一个模型类Class1,我想比较Class1的两个实例是否相同(结构相等) 公共类类别1:IEquatable { 公共字符串Id{get;set;} 公共字符串名称{get;set;} 公共IList类2s{get;set;} 公共布尔等于(1类其他) { 返回QuestName.Equals(其他.QuestName) &&Class2s.OrderBy(c=>c.Id).SequenceEqual(其他.Class2s.OrderBy(c=>c.Id)); //下面的方法很快,但不太准

我有一个模型类
Class1
,我想比较
Class1
的两个实例是否相同(结构相等)

公共类类别1:IEquatable
{
公共字符串Id{get;set;}
公共字符串名称{get;set;}
公共IList类2s{get;set;}
公共布尔等于(1类其他)
{
返回QuestName.Equals(其他.QuestName)
&&Class2s.OrderBy(c=>c.Id).SequenceEqual(其他.Class2s.OrderBy(c=>c.Id));
//下面的方法很快,但不太准确
//因为具有相同哈希代码的两个对象可能相等,也可能不相等
//返回GetHashCode()==other.GetHashCode();
}
公共覆盖布尔等于(对象对象对象)
{
返回obj为Class1
&&等于(obj为1类);
}
公共覆盖int GetHashCode()
{
未经检查
{
int hash=13;
hash=(hash*7)+Name.GetHashCode();
foreach(Class2s.OrderBy中的var c2(c=>c.Id))
{
hash=(hash*7)+c2.GetHashCode();
}
返回散列;
}
}
}
公共类类别2:IEquatable
{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共IList类3s{get;set;}
公共布尔等于(第2类其他)
{
返回Id==other.Id
&&Name.Equals(其他.Name)
&&Class3s.OrderBy(c=>c.Id).SequenceEqual(other.Class3s.OrderBy(c=>c.Id));
}
公共覆盖布尔等于(对象对象对象)
{
返回obj为Class2
&&这等于(obj为第2类);
}
公共覆盖int GetHashCode()
{
未经检查
{
int hash=13;
hash=(hash*7)+Id.GetHashCode();
hash=(hash*7)+Name.GetHashCode();
foreach(Class3s.OrderBy(c=>c.Id)中的变量c3)
{
hash=(hash*7)+c3.GetHashCode();
}
返回散列;
}
}
}
公共类类别3:IEquatable
{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共IList类4s{get;set;}
公共布尔等于(第3类其他)
{
返回Id==other.Id
&&Name.Equals(其他.Name)
&&Class4s.OrderBy(c=>c.Id).SequenceEqual(other.Class4s.OrderBy(c=>c.Id));
}
公共覆盖布尔等于(对象对象对象)
{
返回obj为Class3
&&这等于(obj作为第3类);
}
公共覆盖int GetHashCode()
{
未经检查
{
int hash=13;
hash=(hash*7)+Id.GetHashCode();
hash=(hash*7)+Name.GetHashCode();
foreach(Class4s.OrderBy中的var c(c=>c.Id))
{
hash=(hash*7)+c.GetHashCode();
}                
返回散列;
}
}
}
公共类类别4:IEquatable
{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共布尔等于(第4类其他)
{
返回Id.Equals(其他.Id)
&&Name.Equals(其他名称);
}
公共覆盖布尔等于(对象对象对象)
{
返回obj为Class4
&&这等于(obj为第4类);
}
公共覆盖int GetHashCode()
{
未经检查
{
int hash=13;
hash=(hash*7)+Id.GetHashCode();
hash=(hash*7)+Name.GetHashCode();
返回散列;
}
}
}
我说当:
1.它们具有相同的
名称

2.它们具有相同的
Class2
对象(它们的顺序无关紧要)

两个
Class2
对象相等:
1.它们具有相同的Id
2.他们有相同的名字
3.它们具有相同的
Class3
对象(它们的顺序无关紧要)

两个
Class3
对象相等:
1.它们具有相同的Id
2.他们有相同的名字
3.它们具有相同的
Class4
对象(它们的顺序无关紧要)

两个
Class4
对象相等:
1.它们具有相同的Id
2.他们有相同的名字

我使用
Equals
方法对它们进行比较,并像这样测量运行时间:

Class1 obj1 = GetFirstClass1Object();
Class1 obj2 = GetSecondClass1Object();
var startTime = DateTime.Now;
bool equals = obj1.Equals(obj2);
var elaspedTime = DateTime.Now.Substract(startTime)
上述解决方案工作正常,但速度非常慢。 我知道如果我们展平
obj1
obj2
,它们每个都包含3500个
Class4
对象,比较
obj1
obj2
大约需要12秒

有没有更快的办法?我能不能利用散列来加快速度


另外,
obj1
obj2
中的
Class2
Class3
Class4
对象的数量总是相同的

对列表进行排序以进行比较对我来说似乎效率很低。您可以尝试使用其他方法来比较列表

而不是

Class2s.OrderBy(c => c.Id).SequenceEqual(other.Class2s.OrderBy(c => c.Id)
你可以试试类似的东西

!Class2s.Except(other.Class2s).Any()
如果大多数对象不相等,还可以添加额外的测试,以确保列表在大小不相同时不会循环:

Class2s.Count == other.Class2s.Count && !Class2s.Except(other.Class2s).Any()

当然,您也可以对Class2.Equals()和Class3.Equals方法执行同样的操作

以提供的类为例,考虑以下结构。没有基于您的示例的示例数据可供测试,因此您必须使用现有的数据进行测试

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);
    }
}
公共类类别1:IEquatable{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共IList类2s{get;set;}
公共静态布尔运算符==(左1类,右1类){
返回等于(左、右);
}
公共静态布尔运算符!=(左1类,右1类){
返回!(左==右);
}
公共布尔等于(1类其他){
if(ReferenceEquals(null,other))返回false;
如果(ReferenceEquals(this,other))retu
|                                                                 Method |        Mean | Error | Ratio |
|----------------------------------------------------------------------- |------------:|------:|------:|
|                                                        'Original code' |   535.46 ms |    NA |  1.00 |
|                               'Custom dictionary-based SequenceEquals' | 6,606.23 ms |    NA | 12.34 |
| 'Custom dictionary-based SequenceEquals, classes cache their HashCode' | 1,136.91 ms |    NA |  2.12 |
|                                 'Custom Except()-based SequenceEquals' | 2,281.12 ms |    NA |  4.26 |
|   'Custom Except()-based SequenceEquals, classes cache their HashCode' |   257.46 ms |    NA |  0.48 |
|                                                         'No OrderBy()' |    76.31 ms |    NA |  0.14 |