C# 在不覆盖Equals&;的情况下,测试对象相等性的最佳方法是什么;GetHashCode,或实现IEquatable<;T>;?

C# 在不覆盖Equals&;的情况下,测试对象相等性的最佳方法是什么;GetHashCode,或实现IEquatable<;T>;?,c#,equality,C#,Equality,我想检查两个没有公共属性的对象之间是否相等。但是,我不想重写Equals&GetHashCode方法,也不想实现IEquatable。例如,考虑下面的代码: class Program { static void Main(string[] args) { Guid id = Guid.NewGuid(); string personName = "MyName"; MyClass object1 = new MyClass(id,

我想检查两个没有公共属性的对象之间是否相等。但是,我不想重写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,但是出于各种原因,我不想更改类中的任何代码。另外,这里没有公共财产,所以我无法比较。有没有其他方法来实现这一点

例如,通过反射?或者将对象序列化为JSON,并比较结果字符串

谢谢。

您可以创建一个使用反射的自定义实现:

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;
        }
    }
}
var comparer=new MyClassEqualityComparer();
WriteLine(comparer.Equals(object1,object2));
// ...
公共类MyClassEqualityComparer:EqualityComparer
{
私有静态只读字符串[]\u names={“\u id”,“\u personName”};
私有静态只读字段信息[]\u信息=
typeof(MyClass).GetFields(BindingFlags.Instance | BindingFlags.NonPublic)
.Where(fi=>_names.Contains(fi.Name))
.ToArray();
公共覆盖布尔等于(MyClass x,MyClass y)
{
返回_infos.All(fi=>object.Equals(fi.GetValue(x),fi.GetValue(y));
}
公共覆盖int GetHashCode(MyClass obj)
{
未经检查
{
int hash=31;
foreach(FieldInfo fi in_infos)
{
对象值=fi.GetValue(obj);
hash=(hash*17)+(val==null)?0:val.GetHashCode();
}
返回散列;
}
}
}

在不重写的情况下,无法更改
Equals
实例方法的结果。因此,您需要使用
IEqualityComparer
,它需要显式地传递给需要相等检查的代码。幸运的是,大多数内置集合都接受这样一个相等比较器作为构造函数的参数

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

您可以使用
GetField(“\u 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();
}
void Main()
{
Guid id=Guid.NewGuid();
字符串personName=“MyName”;
MyClass object1=新的MyClass(id,personName);
MyClass object2=新的MyClass(id,personName);
//这将返回false,但我希望它返回true:
Console.WriteLine(object1.Equals(object2));
//[编辑]…通过使用,例如:
WriteLine(MyClassEqualityComparer.Instance.Equals(object1,object2));
}
公共类MyClass
{
私有Guid _id;
私有字符串_personName;
公共MyClass(Guid id,字符串personName)
{
_id=id;
_personName=personName;
}
}
公共类MyClassEqualityComparer:IEqualityComparer
{
private static FieldInfo personNameField=typeof(MyClass).GetField(“_personName”,BindingFlags.Instance | BindingFlags.NonPublic);
private static FieldInfo idField=typeof(MyClass).GetField(“_id”,BindingFlags.Instance | BindingFlags.NonPublic);
公共布尔等于(MyClass o1,MyClass o2)
{
如果(o1==o2)
返回true;
如果(o1==null | | o2==null)
返回false;
字符串名称1=(字符串)personNameField.GetValue(o1);
字符串名称2=(字符串)personNameField.GetValue(o2);
如果(名称1!=名称2)
返回false;
Guid id1=(Guid)idField.GetValue(o1);
Guid id2=(Guid)idField.GetValue(o2);
返回id1==id2;
}
公共int GetHashCode(MyClass o)
{
如果(o==null)
返回0;
字符串名称=(字符串)personNameField.GetValue(o);
Guid id=(Guid)idField.GetValue(o);
返回名称.GetHashCode()^id.GetHashCode();
}
私有MyClassEqualityComparer()
{
}
公共静态只读IEqualityComparer实例=新建MyClassEqualityComparer();
}

您可以在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
    }
}

我不确定我是否理解:如果你想将它们与object.Equals进行比较,你必须重写它,如果不是,你可以为MyClass使用MyEqual方法,然后在那里做任何你想做的事情…»我想穿过那条河。不,不是通过那座明显的桥。不,我也不会游泳,更不用说飞了。事实上,我根本不能动。你能帮我吗?«JSON和XML序列化都帮不了你,因为他们不知道私有字段JOEY,我不确定这是一个暗示,还是一个声明,我错过了一些非常明显的东西。。。!Amittai,我想我的代码不是100%准确-我不一定需要使用Object.Equals()。我可以调用一个方法:ObjectsAreEqual(obj1,obj2),该方法在不使用Object.Equals的情况下检测相等性。但是,如果没有类的公共属性,您将如何实现它?它对Object.Equals没有帮助。Equals问题:来自MSDN:“…与接受IEqualityComparer通用接口的集合类型一起使用。在.NET Framework中,字典泛型集合类型的构造函数接受此接口。“()它显然不能更改
.Equals
实例方法。用常规方法根本不可能做到这一点。(分析API允许这样做,或者您可以对生成的程序集进行后期处理)。