首页 文章

测试对象相等性的最佳方法是什么 - 不重写Equals&GetHashCode,或实现IEquatable <T>?

提问于
浏览
1

我想检查两个没有公共属性的对象之间的相等性 . 但是,我不想重写Equals和GetHashCode方法,或实现IEquatable . 例如,请考虑以下代码:

class Program
{
    static void Main(string[] args)
    {
        Guid id = Guid.NewGuid();
        string personName = "MyName";
        MyClass object1 = new MyClass(id, personName);
        MyClass object2 = new MyClass(id, personName);
        //This returns false, but I'd like it to return true:
        Console.WriteLine(object1.Equals(object2));
        //[edit]...by using, for example:
        Console.WriteLine(ObjectsAreEqual(object1, object2));
    }
}

class MyClass
{
    private Guid _id;
    private string _personName;
    public MyClass(Guid id, string personName)
    {
        _id = id;
        _personName = personName;
    }
}

我知道标准方法是重写Equals和GetHashCode,但由于各种原因,我不想更改类中的任何代码 . 此外,没有公共 property ,所以我无法比较这些 . 有没有其他方法来实现这个?

例如,通过反思?或者可能通过将Objects序列化为JSON,并比较生成的字符串?

谢谢 .

3 回答

  • 1

    如果不重写它,则无法更改 Equals 实例方法的结果 . 因此,您需要使用 IEqualityComparer<T> ,需要将其显式传递给需要进行相等性检查的代码 . 幸运的是,大多数内置集合都接受这样的相等比较器作为其构造函数的参数 .

    如果您不想以任何方式更改原始类,则使用反射实现 IEqualityComparer<T> 似乎是您唯一的选择 .

    您可以使用 GetField("_personName",BindingFlags.NonPublic) 获取 FieldInfo ,然后在其上调用 GetValue 以获取值 .

    void Main()
    {
            Guid id = Guid.NewGuid();
            string personName = "MyName";
            MyClass object1 = new MyClass(id, personName);
            MyClass object2 = new MyClass(id, personName);
            //This returns false, but I'd like it to return true:
            Console.WriteLine(object1.Equals(object2));
            //[edit]...by using, for example:
            Console.WriteLine(MyClassEqualityComparer.Instance.Equals(object1, object2));
    }
    
    public class MyClass
    {
        private Guid _id;
        private string _personName;
        public MyClass(Guid id, string personName)
        {
            _id = id;
            _personName = personName;
        }
    }
    
    public class MyClassEqualityComparer:IEqualityComparer<MyClass>
    {
      private static FieldInfo personNameField=typeof(MyClass).GetField("_personName",BindingFlags.Instance|BindingFlags.NonPublic);
      private static FieldInfo idField=typeof(MyClass).GetField("_id",BindingFlags.Instance|BindingFlags.NonPublic);
    
      public bool Equals(MyClass o1,MyClass o2)
      {
        if(o1==o2)
          return true;
        if(o1==null||o2==null)
          return false;
        string name1=(string)personNameField.GetValue(o1);
        string name2=(string)personNameField.GetValue(o2);
        if(name1!=name2)
          return false;
        Guid id1=(Guid)idField.GetValue(o1);
        Guid id2=(Guid)idField.GetValue(o2);
        return id1==id2;
      }
    
      public int GetHashCode(MyClass o)
      {
        if(o==null)
          return 0;
        string name=(string)personNameField.GetValue(o);
        Guid id=(Guid)idField.GetValue(o);
        return name.GetHashCode()^id.GetHashCode();
      }
    
      private MyClassEqualityComparer()
      {
      }
    
      public static readonly IEqualityComparer<MyClass> Instance=new MyClassEqualityComparer();
    }
    
  • 2

    您可以创建使用反射的自定义IEqualityComparer<T>实现:

    var comparer = new MyClassEqualityComparer();
    Console.WriteLine(comparer.Equals(object1, object2));
    
    // ...
    
    public class MyClassEqualityComparer : EqualityComparer<MyClass>
    {
        private static readonly string[] _names = { "_id", "_personName" };
    
        private static readonly FieldInfo[] _infos =
            typeof(MyClass).GetFields(BindingFlags.Instance | BindingFlags.NonPublic)
                           .Where(fi => _names.Contains(fi.Name))
                           .ToArray();
    
        public override bool Equals(MyClass x, MyClass y)
        {
            return _infos.All(fi => object.Equals(fi.GetValue(x), fi.GetValue(y)));
        }
    
        public override int GetHashCode(MyClass obj)
        {
            unchecked
            {
                int hash = 31;
                foreach (FieldInfo fi in _infos)
                {
                    object val = fi.GetValue(obj);
                    hash = (hash * 17) + ((val == null) ? 0 : val.GetHashCode());
                }
                return hash;
            }
        }
    }
    
  • 2

    您可以向MyClass添加成员函数并检查其中的私有字段:

    class MyClass
    {
        private Guid _id;
        private string _personName;
        public MyClass(Guid id, string personName)
        {
            _id = id;
            _personName = personName;
        }
    
        public bool IsEqual(MyClass otherInstance)
        {
            return (_id == otherInstance._id && _personName == otherInstance._personName);
        }
    }
    

    或者,如果无法更改MyClass实现,则添加扩展方法,并使用反射进行比较,如 CodeInChaos 所述:

    static class MyClassExtensions
    {
        public static bool IsEqual(this MyClass myInstance, MyClass otherInstance)
        {
           // Reflection goes here - read all private field from myInstance     
           // and otherInstance and compare them
        }
    }
    

相关问题