简体   繁体   English

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

[英]What is the best way to test for object equality - without overriding Equals & GetHashCode, or implementing IEquatable<T>?

I'd like to check for equality among two objects that have no Public Properties. 我想检查两个没有公共属性的对象之间的相等性。 However, I don't want to override the Equals & GetHashCode method, or implement IEquatable. 但是,我不想重写Equals和GetHashCode方法,或实现IEquatable。 For example, consider the following code: 例如,请考虑以下代码:

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;
    }
}

I know the standard method is to override Equals & GetHashCode, but for various reasons, I don't want to change any code in the class. 我知道标准方法是重写Equals和GetHashCode,但由于各种原因,我不想更改类中的任何代码。 Plus, there are no public properties, so I can't compare these. 此外,没有公共财产,所以我无法比较这些。 Is there any other way of implementing this? 有没有其他方法来实现这个?

For example, via reflection? 例如,通过反思? Or perhaps by serialising the Objects to JSON, and comparing the resulting strings? 或者可能通过将Objects序列化为JSON,并比较生成的字符串?

Thanks. 谢谢。

You could create a custom IEqualityComparer<T> implementation that uses reflection: 您可以创建一个使用反射的自定义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;
        }
    }
}

You can't change the result of the Equals instance method without overriding it. 如果不重写它,则无法更改Equals实例方法的结果。 So you'll need to use IEqualityComparer<T> which needs to be passed explicitly to the code that requires equality checks. 因此,您需要使用IEqualityComparer<T> ,需要将其显式传递给需要进行相等性检查的代码。 Luckily most built in collections accept such an equality comparer as a parameter to their constructor. 幸运的是,大多数内置集合都接受这样的相等比较器作为其构造函数的参数。

Implementing IEqualityComparer<T> with reflection seems to be your only option if you don't want to change the original class in any way. 如果您不想以任何方式更改原始类,则使用反射实现IEqualityComparer<T>似乎是您唯一的选择。

You can get the FieldInfo using GetField("_personName",BindingFlags.NonPublic) and then call GetValue on it to get the value. 您可以使用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();
}

You could add a member function to MyClass and check the private fields there: 您可以向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);
    }
}

or add extension method if you can't change the MyClass implementation, and compare using reflection as CodeInChaos described: 或者,如果无法更改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
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 重写Object.Equals并实现IEquatable &lt;&gt;时无法访问的代码? - Unreachable code when overriding Object.Equals and implementing IEquatable<>? 重写Equals和GetHashCode不一定会覆盖相等重载运算符 - Overriding Equals and GetHashCode doesn't necessarily override equality overloading operator IEquatable 和只是覆盖 Object.Equals() 有什么区别? - What's the difference between IEquatable and just overriding Object.Equals()? 实现Equals和GetHashCode-一种更简单的方法 - Implementing Equals and GetHashCode - an easier way 实现EqualityCompare与覆盖GetHashCode和Equals - Implementing EqualityCompare vs overriding GetHashCode and Equals quaqua <T> -C#中.Equals(object obj)的最佳做法替代 - IEquatable<T> - Best practice override for .Equals(object obj) in C# 使用 GetHashCode 在 Equals 覆盖中测试相等性 - Using GetHashCode to test equality in Equals override IEquatable上的重载等于 - Overriding Equals on IEquatable IEquatable之间的差异 <T> ,IEqualityComparer <T> ,并在自定义对象集合上使用LINQ时覆盖.Equals()? - Differences between IEquatable<T>, IEqualityComparer<T>, and overriding .Equals() when using LINQ on a custom object collection? 为IEqualityComparer实现GetHashCode <T> 条件平等 - Implementing GetHashCode for IEqualityComparer<T> with conditional equality
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM