2012-01-19 73 views
5

我有一个有很多属性的类。浅拷贝足以完全复制对象。简单地在c中比较两个对象的属性#

我需要比较一个对象只是为了检查它是否包含与另一个完全相同的值。

我的想法:

第一个也是最明显的解决方案就是创建每个属性,一个接一个比较庞大的方法块。

第二,将序列化每个对象和散列文件或做一些对它的md5校验和。 (这实际上工作)。

第三种是对物体进行某种反射,这会使第一种选择自动化,但是会增加复杂性。

速度并不是真正的问题。

有兴趣听​​到思想或任何其他方法我失踪做这样的事情。

编辑: 谢谢大家。我的解决方案:(修改为现在是递归通用项目)。

public static bool IsSame<T>(T objA, T objB) 
     { 
      var type = typeof(T); 
      var properties = type.GetProperties(); 
      foreach (var pi in properties.Where(a => a.CanRead)) 
      { 
       if (pi.Name == "Item") 
       { 
        var piCount = properties.FirstOrDefault(a => a.Name == "Count"); 
        int count = -1; 
        if (piCount != null && piCount.PropertyType == typeof(System.Int32)) 
         count = (int)piCount.GetValue(objA, null); 
        if (count > 0) 
        { 
         for (int i = 0; i < count; i++) 
         { 
          dynamic child1 = pi.GetValue(objA, new object[] { i }); 
          dynamic child2 = pi.GetValue(objB, new object[] { i }); 
          return IsSame(child1, child2); 
         } 
        } 
       } 
       else 
       { 
        var val1 = type.GetProperty(pi.Name).GetValue(objA, null); 
        var val2 = type.GetProperty(pi.Name).GetValue(objB, null); 
        if (val1 != val2 && (val1 == null || !val1.Equals(val2))) 
         return false; 
       } 
      } 
      return true; 
     } 
+2

我会去反思,除非类结构不容易改变。如果它*不会*改变,我会选择第一个解决方案,而不是(易于阅读,易于维护) – Alex

+0

http://stackoverflow.com/questions/506096/comparing-object-properties-in-c-sharp有用反射解决方案。 – Giedrius

回答

6

大多数串行旨在确保序列化和反序列化过程中的数据保持其完整性,不产生一致的序列化格式。我会避免为此目的使用序列化。

您可以考虑实施IEquatable,让每个实例能够将自己与相同类型的实例进行比较。或者一个班级为你实施IEqualityComparer做比较。他们如何做这个比较可能是一个接一个地比较特性或使用反思的“大方法”。

反射可以是一种相当快速和简单的方式来实现您的目标,但可能导致问题(例如,如果有人添加属性到您的类型不应包括比较相等),但显然相反是也是如此(有人添加了一个应该检查相等性的属性,但是不会更新相等性比较)。您使用哪种方法通常应由团队对每种方法的适应程度以及班级的使用环境决定。

在你的情况,我可能会建议使用基于反射的方法,因为你想检查一个浅克隆操作的结果,所以所有的属性应该是平等的。另外,我建议使用MemberwiseClone方法来创建克隆,这将减少对这种严格测试的需求。

+3

我想你会想IEquatable而不是IComparable。 IComparable用于排序,而不是等价。 –

+0

@ForgottenSemicolon谢谢,回复更新。 –

+0

+1提供良好的接口来查看实现。 – pseudocoder

1

另一个想法是,如果属性返回简单值类型,则可以将它们分组为它们自己的不可变值类型。例如,如果你有一个客户的属性string Address1string Address2string Citystring Statestring Zip,你可以创建一个值类型地址,它实现了自己的相等比较器并使用它。

不确定这是否适用于您的情况,但我发现,当我有一个具有很多属性的类时,通常可以这样做,并且它倾向于使我的类更简单,更容易推理关于。

6

第三个选项(反射)将是最慢的,但它也是最强大/可测试的。

哈希码与第一个选项非常相似,因为您必须调用所有成员变量,所以1和2会要求您在每次修改成员变量列表时都更新.Equals(obj).GenerateHash()方法。

这里是一些代码,让你开始:

 foreach (FieldInfo field in this.GetType().GetFields()) 
     { 
      if (o[field.Name] == null) 
      { 
       if (!field.GetValue(this).Equals(o[field.Name])) 
        return false; 
      } 
      else 
      { 
       return false; 
      } 
     } 

     return true; 
1

如果您序列化,你必须指定每个字段/属性的序列化,以确保所有数据序列化的int相同的顺序。但是你要实现的只是在外部类中实现GetHashCode()。

GetHashCode()有一些关于如何覆盖它的例子,所以先看看那里。

如果你的班级有很多字段,并且你不想手动编写所有的代码。您可以使用反射来自动生成代码。如果您的类不时发生变化,那么您可以在T4模板中使用GetHashCode()实现创建一个部分类。