比较两个自定义对象 - C#

7

我需要在基类中编写一个通用方法,该方法接受两个对象作为参数,并将它们进行比较以确定它们是否相等。

例如:

public abstract class BaseData
{

  public bool AreEqual(object O1, object O2)
  {
    //Need to implement this
  }
}

public class DataTypeOne : BaseData
{
  public string Name;
  public string Address;
}

public class DataTypeTwo : BaseData
{
  public int CustId;
  public string CustName;
}
AreEqual()方法将接受两个DataTypeOne实例或两个DataTypeTwo实例。我的猜测是我需要使用反射?如果可以更易读/简洁,我可以使用LINQ。 编辑: 我想在基类中实现此方法的原因是因为项目有限制。有大量的开发人员正在开发派生类。通过在基类中实现此方法,我试图让他们少担心一件事情。

为什么不重写Object.Equals方法? - Paco
为什么需要在基类中实现AreEqual(而且不使用泛型)?如果AreEqual是抽象的,并且DataTypeOne和DataTypeTwo实现了AreEqual,那么这就是一个更清晰的解决方案。简而言之:共同的AreEqual方法的原因是什么? - boj
6个回答

14

(假定你想要比较两个对象的所有字段是否相等。)

通常情况下,你不会费心使用反射来比较它们,你可以自己逐个比较每个字段。为此,IEquatable<T>接口存在,你也可能需要重写相关类型的Object.Equals()方法。例如:

public class DataTypeTwo : BaseData, IEquatable<DataTypeTwo>
{
    public int CustId;
    public string CustName;

    public override int GetHashCode()
    {
        return CustId ^ CustName.GetHashCode(); // or whatever
    }

    public override bool Equals(object other)
    {
        return this.Equals(other as DataTypeTwo);
    }

    public bool Equals(DataTypeTwo other)
    {
        return (other != null &&
                other.CustId == this.CustId &&
                other.CustName == this.CustName);
    }
}

另外,考虑一下你的类型是否更适合用struct。值类型通过逐字段比较自动比较相等。

请注意,通过重写Equals方法,您基本上实现了您尝试使用“主要等于方法”方案实现的目标(至少我是这样认为的)。也就是说,使用DataTypeTwo的人将能够自然地测试相等性,而不必知道有关您的API的任何特殊信息--他们将像使用其他东西一样使用Equals

编辑:感谢Jared提醒我关于GetHashCode的问题。您还需要重写它,通过确保任何两个“相等”的对象也返回相同的哈希代码,以维护在哈希表中正常的行为。


1
你仍需要覆盖 GetHashCode。 - JaredPar
为什么你需要GetHashCode,Equals不足够吗? - Ahmed
1
请不要仅仅为了改变相等比较行为而将类更改为结构体。像笛卡尔坐标这样的小型数据类型,其预期以某种“按值”方式运作是可以的;但是,其他程序员认为应该是类的数据结构不应该被转换为结构体进行按值比较 - 这也会在初始化和(最重要的)赋值方面隐式地改变行为。 - perfectionist

4

这是我使用反射得到的结果,希望能对你有所帮助。

public bool AreEqual(object obj)
    {
        bool returnVal = true;

        if (this.GetType() == obj.GetType())
        {
            FieldInfo[] fields = this.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

            foreach (FieldInfo field in fields)
            {
                if(field.GetValue(this) != field.GetValue(obj))
                {
                    returnVal = false;
                    break;
                }
            }
        }
        else
            returnVal = false;

        return returnVal;
    }

谢谢Crispy,这正是我在寻找的! - Lanceomagnifico

2

不要这样做。继承是正解,每个类都应在需要的情况下重写Equal和GetHashCode方法。

也许你现在可以从这些开发人员那里得到一些工作成果,但当 产品需要维护时,这种做法会给你带来麻烦。

说真的,试着找另一种方式来帮助吧。


1

我可能会这样做:

public abstract class BaseData : IEquatable<BaseData>
{
    public abstract bool Equals(BaseData other);
}

public class DataTypeOne : BaseData
{
    public string Name;
    public string Address;

    public override bool Equals(BaseData other)
    {
        var o = other as DataTypeOne;
        if(o == null)
            return false;
        return Name.Equals(o.Name) && Address.Equals(o.Address);
    }
}

public class DataTypeTwo : BaseData
{
    public int CustId;
    public string CustName;

    public override bool Equals(BaseData other)
    {
        var o = other as DataTypeTwo;
        if (o == null)
            return false;
        return CustId == o.CustId && CustName.Equals(o.CustName);
    }
}

也许解释一下为什么这很“疯狂”会很好。 - madcolor
我认为没有必要比较 DataTypeOne 和 DataTypeTwo,因为它们永远不会相等。换句话说,我可能会在 DataTypeOne 中实现 IEquatable<DataTypeOne>,或者在 DataTypeTwo 中实现 IEquatable<DataTypeTwo>。或者更有可能的是,我会使用 Linq 或其他方法查找特定 CustId 的特定 DataTypeTwo,而不需要 IEquatable 或重写 Equals 等操作。 - Svish

0
public void CompareTwoObjects()
{
    try {   
        byte[] btArray = ObjectToByteArray(object1);   //object1 is you custom object1
        byte[] btArray2 = ObjectToByteArray(object2); //object2 is you custom object2
        bool result = ByteArrayCompare(btArray, btArray2);
    } catch (Exception ex) {
        throw ex;
    }
}

public byte[] ObjectToByteArray(object _Object)
{
    try {
        // create new memory stream
        System.IO.MemoryStream _MemoryStream = new System.IO.MemoryStream();

        // create new BinaryFormatter
        System.Runtime.Serialization.Formatters.Binary.BinaryFormatter _BinaryFormatter
            = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();

        // Serializes an object, or graph of connected objects, to the given stream.
        _BinaryFormatter.Serialize(_MemoryStream, _Object);

        // convert stream to byte array and return
        return _MemoryStream.ToArray();
    } catch (Exception _Exception) {
        // Error
        Console.WriteLine("Exception caught in process: {0}", _Exception.ToString());
    }

    // Error occured, return null
    return null;
}

public bool ByteArrayCompare(byte[] a1, byte[] a2)
{
    if (a1.Length != a2.Length)
        return false;

    for (int i = 0; i < a1.Length; i++)
        if (a1[i] != a2[i])
            return false;

    return true;
}

1
感谢您发布答案!虽然代码片段可以回答问题,但添加一些额外的信息,如解释等,仍然很棒。 - j0k

0

是的,您将不得不使用反射,因为基类对派生类一无所知。但是为什么要在基类中实现该函数呢?为什么不在派生类中实现呢?

此外,有一种标准方法可以通过重写Object.GetHashCode()和Object.Equals()来实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接