9

I have a dialog, when spawned it gets populated with the data in an object model. At this point the data is copied and stored in a "backup" object model. When the user has finished making their changes, and click "ok" to dismiss the dialog, I need a quick way of comparing the backup object model with the live one - if anything is changed I can create the user a new undo state.

I don't want to have to go and write comparison function for every single class in the object model if possible.

If I serialised both object models and they were identical but stored in different memory locations would they be equal? Does some simple way exist to compare two serialised object models?

DrLazer
  • 2,805
  • 3
  • 41
  • 52
  • 1
    Could you provide more detail about what technologies you are using. Are you using WPF, WinForms, or Silverlight? There is a very easy way to do this with Entity Framework or WCF RIA Services. – Andrew Garrison Jan 06 '11 at 15:40
  • hi Andrew - I an using .net 3.5 and WPF with c# express 2008 – DrLazer Jan 06 '11 at 15:46
  • 1
    Are you using Entity Framework, POCO, or something else? – Andrew Garrison Jan 06 '11 at 15:51
  • No, I haven't heard of those before. – DrLazer Jan 06 '11 at 15:54
  • 1
    Then, I'm guessing you are using POCO (Plain-old-CLR-objects). I think your idea may work - just serialize them, then maybe hash that string, and the compare the hashes between two object models.I can't guarantee that this would work reliably, but I think it would be worthwhile to investigate! – Andrew Garrison Jan 06 '11 at 15:57
  • Yeah I certainly am using vanilla CLR objects. So hand on a second, I can serialize an object to a hash string? – DrLazer Jan 06 '11 at 15:59
  • 1
    Well, first serialize the object, then compute a hash of the serialized data. Now, keep in mind that the hash will not be unique - there could be two different object models that hash to the same value, although I feel that this is rather unlikely. – Andrew Garrison Jan 06 '11 at 16:08
  • ill give it a bash, whats the worst that can happen. Ill post on here if it works out – DrLazer Jan 06 '11 at 16:12

3 Answers3

12

I didn't bother with a hash string but just a straight Binary serialisation works wonders. When the dialog opens serialise the object model.

BinaryFormatter formatter = new BinaryFormatter();
m_backupStream = new MemoryStream();
formatter.Serialize(m_backupStream,m_objectModel);

Then if the user adds to the object model using available controls (or not). When the dialog closes you can compare to the original serialisation with a new one - this for me is how i decide whether or not an Undo state is required.

BinaryFormatter formatter = new BinaryFormatter();
MemoryStream liveStream = new MemoryStream();
formatter.Serialize(liveStream,m_objectModel);
byte[] streamOneBytes = liveStream.ToArray();
byte[] streamTwoBytes = m_backupStream.ToArray();
if(!CompareArrays(streamOneBytes, streamTwoBytes))
    AddUndoState();

And the compare arrays function incase anybody needs it - prob not the best way of comparing two arrays im sure.

private bool CompareArrays(byte[] a, byte[] b)
{
    if (a.Length != b.Length)
       return false;

    for (int i = 0; i < a.Length;i++)
    {
       if (a[i] != b[i])
        return false;
    }
    return true;
}
DrLazer
  • 2,805
  • 3
  • 41
  • 52
  • why not just serialize the arrays and compare the strings/bytes? If they're identical wouldn't their serialization yield the same thing? – Aelphaeis May 12 '14 at 15:42
7

I'd say the best way is to implement the equality operators on all classes in your model (which is usually a good idea anyway if you're going to do comparisons).

class Book
{
    public string Title { get; set; }
    public string Author { get; set; }
    public ICollection<Chapter> Chapters { get; set; }

    public bool Equals(Book other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Equals(other.Title, Title) && Equals(other.Author, Author) && Equals(other.Chapters, Chapters);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != typeof (Book)) return false;
        return Equals((Book) obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int result = (Title != null ? Title.GetHashCode() : 0);
            result = (result*397) ^ (Author != null ? Author.GetHashCode() : 0);
            result = (result*397) ^ (Chapters != null ? Chapters.GetHashCode() : 0);
            return result;
        }
    }
}

This snippet is auto-generated by ReSharper, but you can use this as a basis. Basically you will have to extend the non overriden Equals method with your custom comparison logic.

For instance, you might want to use SequenceEquals from the Linq extensions to check if the chapters collection is equal in sequence.

Comparing two books will now be as simple as saying:

Book book1 = new Book();
Book book2 = new Book();

book1.Title = "A book!";
book2.Title = "A book!";

bool equality = book1.Equals(book2); // returns true

book2.Title = "A different Title";
equality = book1.Equals(book2); // returns false

Keep in mind that there's another way of implementing equality: the System.IEquatable, which is used by various classes in the System.Collections namespace for determining equality.

I'd say check that out as well and you're well on your way!

Erik van Brakel
  • 23,220
  • 2
  • 52
  • 66
  • that answers the question of "how can i compare two objects for equality" im after a quick and easy way to compare an entire object model, without writing comparison code on each class. – DrLazer Jan 06 '11 at 15:57
  • As suggested, Reshaper makes generating these comparisons very easy. https://www.jetbrains.com/help/resharper/2016.2/Code_Generation__Equality_Members.html – Andy Reed Nov 18 '16 at 09:31
3

I understand your question to be how one can compare two objects for value equality (as opposed to reference equality) without prior knowledge of the types, such as if they implement IEquatable or override Equals.

To do this I recommend two options:

A. Use an all-purpose serialization class to serialize both objects and compare their value. For example I have a class called XmlSerializer that takes any object and serializes its public properties as an XML document. Two objects that have the same values and possibly the same reference will have the same values in this sense.

B. Using reflection, compare the values of all of the properties of both objects, like:

bool Equal(object a, object b)
{
    // They're both null.
    if (a == null && b == null) return true;
    // One is null, so they can't be the same.
    if (a == null || b == null) return false;
    // How can they be the same if they're different types?
    if (a.GetType() != b.GetType()) return false; 
    var Props = a.GetType().GetProperties();
    foreach(var Prop in Props)
    {
        // See notes *
        var aPropValue = Prop.GetValue(a) ?? string.Empty;
        var bPropValue = Prop.GetValue(b) ?? string.Empty;
        if(aPropValue.ToString() != bPropValue.ToString())
            return false;
    }
    return true;
}

Here we're assuming that we can easily compare the properties, like if they all implement IConvertible, or correctly override ToString. If that's not the case I would check if they implement IConvertible and if not, recursively call Equal() on the properties.

This only works if you're content with comparing public properties. Of course you COULD check private and protected fields and properties too, but if you know so little about the objects you're probably asking for trouble but doing so.

Yvan
  • 31
  • 1
  • Thanks for the info, I did think about using reflection but wanted to make a clear emphasis on not having to write lots of compare code. Plus it probably needs to be extensible for when the requirements change and I have to add in new objects (or someone else does) :S – DrLazer Jan 17 '11 at 14:59
  • Thanks.It helped me compare two different objects. – gouravm Sep 27 '22 at 08:11