I would personally avoid XOR – it means that any two equal values will result in 0 – so hash(1, 1) == hash(2, 2) == hash(3, 3) etc. Also hash(5, 0) == hash(0, 5) etc which may come up occasionally. I have deliberately used it for set hashing – if you want to hash a sequence of items and you don’t care about the ordering, it’s nice.
I usually use:
unchecked
{
int hash = 17;
hash = hash * 31 + firstField.GetHashCode();
hash = hash * 31 + secondField.GetHashCode();
return hash;
}
That’s the form that Josh Bloch suggests in Effective Java. Last time I answered a similar question I managed to find an article where this was discussed in detail – IIRC, no-one really knows why it works well, but it does. It’s also easy to remember, easy to implement, and easy to extend to any number of fields.