2009-04-28 3 views
6

Мне нужно написать общий метод в базовом классе, который принимал бы 2 объекта в качестве параметров и сравнивал их для равенства.Сравнение 2 пользовательских объектов - C#

Пример:

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() Метод будет принимать 2 экземпляра DataTypeOne или 2 экземпляра DataTypeTwo.

Я думаю, что мне нужно использовать Reflection? Я могу использовать LINQ, если он может быть более читаемым/сжатым.

EDIT: Причина, по которой я хотел бы реализовать этот метод в базовом классе, связана с ограничениями проекта. На производных классах работает большое количество разработчиков. Внедряя это в базовый класс, я стараюсь, чтобы у них было меньше дел, о которых они могли беспокоиться.

+0

Почему вы не переопределяете Object.Equals? – Paco

+0

Зачем вам нужно реализовать AreEqual в базовом классе (и почему без дженериков)? Если AreEqual является абстрактным, а DataTypeOne и DataTypeTwo реализует AreEqual, то это более чистое решение. Короче говоря: в чем причина общего метода AreEqual? – boj

ответ

13

(Предполагая, что вы хотите, чтобы сравнить все поля двух объектов на равенство.)

Обычно, вы бы не заморачиваться с помощью отражения для этого, вы бы просто сравнить каждое поле самостоятельно. Для этой цели существует интерфейс 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, как если бы они были с другими вещами.

EDIT: Спасибо Джареду за то, что он напомнил мне о GetHashCode. Вы также захотите переопределить его для поддержания нормального поведения в хэш-таблицах, убедившись, что любые два «равных» объекта также возвращают один и тот же хэш-код.

+1

Вам все еще нужно отменить GetHashCode – JaredPar

+0

Зачем вам нужен GetHashCode, Equals недостаточно? –

+1

Пожалуйста, никогда не меняйте класс на структуру только ради изменения поведения сравнения равенства. Крошечные типы данных, такие как декартовы координаты, которые, как ожидается, будут вести себя определенным способом «по значению», прекрасны; но структуры данных, которые другой программист будет считать классом, не должны встраиваться в структуры для сравнения по значению - вы также будете невидимо изменять поведение по отношению к. инициализация и (самое главное) назначение. – perfectionist

0

Да, вам придется использовать отражение, потому что базовый класс ничего не знает о производных классах. Но почему вы хотите реализовать эту функцию в базовом классе? Почему не в производных классах?

Далее существует стандартный способ сделать это, переопределив Object.GetHashCode() и Object.Equals().

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); 
    } 
} 
+0

Возможно, объяснение, почему это «безумный», было бы неплохо. – madcolor

+0

Ну, я просто думаю, что на самом деле не нужно сравнивать DataTypeOne и DataTypeTwo, так как они никогда не будут равными. Другими словами, я бы, вероятно, просто реализовал IEquatable в DataTypeOne и/или IEquatable в DataTypeTwo. Или, скорее всего, я бы просто использовал linq или что-то, чтобы, например, искать компонент DataTypeTwo с конкретным CustId и не беспокоиться о IEquatable или переопределять Equals или что-то в этом роде. – Svish

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

Спасибо Crispy, что было * точно *, что я искал! – Lanceomagnifico

2

Не делайте этого. Наследование - это способ пойти, и каждый класс должен переопределить Equal и GetHashCode там, где это необходимо.

Возможно, теперь вы сделаете какую-то работу с этими разработчиками, но я вернусь, чтобы укусить вас в задницу в будущем, когда необходимо поддерживать продукт.

Серьезно, просто попробуйте найти другой способ помочь.

+1

+1, иногда единственный правильный ответ на вопрос «Как?» это «Не делать». – jwg

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

Смежные вопросы