views:

134

answers:

4

see also Hows to quick check if data transfer two objects have equal properties in C#?

I have lot of Data Transfer Objects (DTO) that each contains lots of simple fields. I need to implement Equals on all of them (so I can write some unit tests off transporting them var WCF).

The code I am using is:

public override bool Equals(object rhs)
{

    RequestArguments other = rhs as RequestArguments;

    return
       other != null && 
       other.m_RequestId.Equals(RequestId) && 
       other.m_Type.Equals(m_Type) && 
       other.m_Parameters.Equals(m_Parameters) && 
       other.m_user.Equals(m_user);
}

There must be a better way!... (listing all the fields is rather asking for errors and maintenance problems)

E.g. we have Object. MemberwiseClone() to help with the Cloning() case, but I cannot find anything to help with Equals. We are running in full trust so a reflection based solution is one answer, but I rather not reinvent the wheel.

(Sorry we don’t generate the DTO from a domain-specific language otherwise this sort of thing would be easy! Also I am not able to change the build system to add another step)

A: 

You can have a concept of an object hash - whenever an object changes, you pay the price of updating the hash (where the hash is literally a hash of all concatenated properties). Then, if you have a bunch of objects that rarely change, it's really cheap to compare them. The price, of course, is then paid at object editing time.

Alex
That doesn't solve anything, you still need to hash all the fields manually.
arul
this does not let me test if the objects are transported ok with WCF, it just adds more code to do wronge!
Ian Ringrose
Besides, you'd need to use a perfect hash to avoid collisions.
arul
+1  A: 

An option is to use reflection to get all the available fields and then get and compare their values on the desired objects. This would give you a generic solution but you would have quite a work to do, probably using hashes as Alex suggests is a cleaner solution.

EDIT: Here is a simple example of comparing objects using reflection, it looks at properties instead of fields but you get the idea: http://www.willasrari.com/blog/use-systemreflection-for-comparing-custom-objects/000257.aspx

Konamiman
What work to do exactly? Just mark the DTO fields with a code attribute and use that during reflection to pick out the fields you wish to compare. Hell you could even make DTOEquals method so that you didn't have to play with the equals at all if you have objects.
Spence
I was hoping that someone would point me at a pre-canned implementation of something like this.
Ian Ringrose
A: 

Edit: sorry, I didn't notice that you are asking for serialization testing. So this approach definitely doesn't work for you.


There is another "dirty" way. If your object is serializable anyway, you can serialize them and compare the resulting streams.

This is rather slow, but should be quite reliable and easy to implement.

We are doing this sometimes to check if someone changed any data in an editor.

Stefan Steinegger
yes, but I wish to TEST the serialization so this is not a good solution in MY case.
Ian Ringrose
I wrote a serialization tester which creates objects from scratch, serializes them and compares them using lots of reflection. It is highly configurable and consists of two thousand or more lines of code. I wanted to release it under LGPL, but have to ask my boss first and clean it a bit. So it doesn't help you for now.
Stefan Steinegger
+3  A: 

Funny you should ask, I recently published some code for doing exactly that. Check out my MemberwiseEqualityComparer to see if it fits your needs.

It's really easy to use and quite efficient too. It uses IL-emit to generate the entire Equals and GetHashCode function on the first run (once for each type used). It will compare each field (private or public) of the given object using the default equality comparer for that type (EqualityComparer.Default). We've been using it in production for a while and it seems stable but I'll leave no guarantees =)

It takes care of all those pescy edge-cases that you rarely think of when you're rolling your own equals method (ie, you can't comparer your own object with null unless you've boxed it in an object first and lot's off more null-related issues).

I've been meaning to write a blog post about it but haven't gotten around to it yet. The code is a bit undocumented but if you like it I could clean it up a bit.

public override int GetHashCode()
{
    return MemberwiseEqualityComparer<Foo>.Default.GetHashCode(this);
}

public override bool Equals(object obj)
{
    if (obj == null)
        return false;

    return Equals(obj as Foo);
}

public override bool Equals(Foo other)
{
    return MemberwiseEqualityComparer<Foo>.Default.Equals(this, other);
}

The MemberwiseEqualityComparer is released under the MIT license meaining you can do pretty much whatever you want with it, including using it in proprietary solutions without changing you licensing a bit.

Markus Olsson
Thanks, this looks great; however for me to be allowed to use it, it would have to be on a standard open source code site, be well documented and have good unit test coverage. If there is not already an established open source solution to this problem, you have the chance of becoming that solution.
Ian Ringrose
Hi Ian. If you check down at the bottom of the page you'll see that I've licensed it under the MIT-License. One of the most permissive open source licenses available so there shouldn't be any problems using it =) I'll update my answer to make a note of it.
Markus Olsson
Hi again Ian. Seems I was a bit hasty in my response and answered before I had read your comment in its entirety. Sorry for that. I could definitely publish the unit tests that we've written for the EqualityComparer at work but I'll have to clear it with my company first so that I've got the permission to license it under the MIT-License. I'll get right on that. Check my site for updates. As for a standard open source code site I think this piece of code is a bit to small to host on a large code hosting site like Google Code, don't you?
Markus Olsson
@Markus I have just seen http://stackoverflow.com/questions/986572/hows-to-quick-check-if-data-transfer-two-objects-have-equal-properties-in-c that this question is a duplicate of. You may wish to add your answer to the older question
Ian Ringrose
@Ian: I've done so now (http://stackoverflow.com/questions/986572/hows-to-quick-check-if-data-transfer-two-objects-have-equal-properties-in-c/1831921#1831921)
Markus Olsson