Q:

What is the purpose of the tilde in this situation?

What could be the purpose of the tilde in this code block?
public override int GetHashCode()
    {
      return ~this.DimensionId.Id ^ this.ElementId.Id;
    }

^ Operator (C# Reference)
Visual Studio 2010
Binary ^ operators are predefined for the integral types and bool. For integral types, ^ computes the bitwise exclusive-OR of its operands. For bool operands, ^ computes the logical exclusive-or of its operands; that is, the result is true if and only if exactly one of its operands is true.
~ Operator (C# Reference)
Visual Studio 2010
The ~ operator performs a bitwise complement operation on its operand, which has the effect of reversing each bit. Bitwise complement operators are predefined for int, uint, long, and ulong.
The ~ (tilde) operator performs a bitwise complement on its single integer operand. (The ~ operator is therefore a unary operator, like ! and the unary -, &, and * operators.) Complementing a number means to change all the 0 bits to 1 and all the 1s to 0s
What would be a reason why it would be used in this context (as opposed to simply excluding it)?

A:

It's simply one way of generating a hash code. I'm not a huge fan of XORs in hash codes unless you want something that's order-independent, but it's a reasonable way of flipping bits in a reasonably arbitrary but repeatable way.
Basically you've got two 32-bit values here, which you need to combine in some form to create another 32-bit value. The code could have just XORed the values together without any bitwise complement:
return DimensionId.Id ^ ElementId.Id;

... but that would always give zero for cases where ElementId.Id == DimensionId.Id, which probably isn't ideal. On the other hand, we now always end up with -1 if the two IDs are the same, as noted in comments (doh!). On the other hand, it makes the pair {6, 4} have a different hash code to {4, 6} whereas a simple XOR doesn't... it makes the ordering important, in other words. Again, that could be important if your real identifiers are likely to be taken from a relatively small pool.
The XOR itself makes sure that a change to any bit in either ID makes a difference to the final hashcode.
Personally I normally follow Josh Bloch's pattern from effective Java, e.g.
unchecked
{
    int hash = 17;
    hash = hash * 31 + DimensionId.Id;
    hash = hash * 31 + ElementId.Id;
    return hash;
}

... but that's just because of some of the properties of hashing that way1, and it doesn't make the implementation you've shown "wrong" in any sense.

1 It seems to work pretty well at producing distinct values in a number of common scenarios. Obviously it can't prevent hash collisions, but if your IDs are actually generated from a sequence of 1, 2, 3... then this will do better at real-life collisions that an XOR. I did see a web page analyzing this approach and which numbers work well etc, but I can't remember where.

