3

Sometimes I need value objects without fields (message headers, schemas, etc.), for example:

abstract class RequestHeader
{
}

sealed class FirstRequestHeader : RequestHeader
{
}

I use them in methods like:

partial class Server
{
    private readonly IReadOnlyDictionary<RequestHeader, Action<object>> requestMap;

    public void ProcessRequest(RequestHeader header, object request)
    {
        requestMap[header](request);
    }
}

In this case default implementation of GetHashCode and Equals methods totally fits my needs, because I can use singletons.

But since FirstRequestHeader is an immutable value object I want it to behave like a real value object:

var a = new FirstRequestHeader();
var b = new FirstRequestHeader();
Console.WriteLine(a == b &&
    a.Equals(b) &&
    a.GetHashCode() == b.GetHashCode()); // False, but should be True

Overriding == operator and Equals method is easy.

But what is correct or recommended way of overriding GetHashCode method in this case?

I can expect some answers (all with some drawbacks):

  • hardcode constant hashcode for each type
  • generate one each execution and save it in a static field
  • use type's hashcode through GetType method
  • avoid empty objects (add a field)

But no assumption was confirmed by searching

So, what would you do?

astef
  • 8,575
  • 4
  • 56
  • 95
  • 1
    `GetHashCode` should go along with `Equals`. It should reflect how `Equals` is implemented. So how did you implement your equals method? Can you post that code? – Sriram Sakthivel Aug 20 '14 at 11:18
  • @SriramSakthivel I meant the very common `Equals` implementation from msdn: if not null and not a wrong type, return true (since there is no fields to match) – astef Aug 20 '14 at 11:23
  • So that means you're essentially you're comparing the type object. In that case you need to return `type.GetHashCode` – Sriram Sakthivel Aug 20 '14 at 11:49
  • That's an option, but not the cleanest one. Reflection overhead (not very big, can be cached). Hashcode equality between two different objects is strange (it's better to transform type's hash somehow). Look at ChaosPandion's answer - the same result with less effort and looks cleaner. – astef Aug 20 '14 at 12:07
  • I don't understand what *Reflection overhead* ? If you check type in `Equals` method don't you have the same overhead there? – Sriram Sakthivel Aug 20 '14 at 12:11
  • you can just return a constant hash code per type. But your design is really flawed. there is absolutely no justification for a class that represents a value object, if it has no values. – AK_ Aug 20 '14 at 12:13
  • also, in .NET this: obj.GetType().FullName is very, very fast... reflections become an issue only when you start invoking based on it. – AK_ Aug 20 '14 at 12:15
  • Fast or not - it is relative measure. Comparing to the singleton-based solution, overhead exists. Also, there are ideological issues with reflection-based solution. I can't explain in few words. Google about why don't use reflection when other solutions exists – astef Aug 20 '14 at 13:07
  • @AK_ You're wrong about empty objects, there's a lot of applications of them. Technically, there's no way of creating an object without a value. Object by itself is a value by it's nature. In this question I'm talking about absence of fields which is absolutely normal. You can ask a separate question about them - I can explain in detail – astef Aug 20 '14 at 13:13
  • @astef of course a class without fields can make sense, for example System.Math , or Business Logic classes, etc. sometime classes can have only read-only properties and make sense. But value objects, as well as DTOs, or POCOs, that don't have values, make absolutely no sense at all. What you are trying to do, is called "Tagging". You want to say: this class is actually has that meaning, or these classes all represent this concept. And doing that with unnecessary empty classes in **C#** is a mistake. – AK_ Aug 20 '14 at 13:34
  • regarding the reflections, I agree. The singletone object solution is more elegant... Also reflections make sense when you want to talk about, or discover an object's actual type and implementation. If there are simpler solutions then they are preferable – AK_ Aug 20 '14 at 13:37

3 Answers3

3

If there is no data associated with the class then make only one instance.

sealed class FirstRequestHeader : RequestHeader
{
    public static readonly FirstRequestHeader Value = new FirstRequestHeader();

    private FirstRequestHeader()
    {

    }
}
ChaosPandion
  • 77,506
  • 18
  • 119
  • 157
  • Interesting thing - I'm currently doing so, but I didn't closed the constructor and felt insecure with possible errors, and that's why this question. So, I suppose this is the answer (though it was easy one :P) – astef Aug 20 '14 at 11:20
2

hardcode constant hashcode for each type

If you want two "identical" objects to be treated as equal (and with no fields or your instances are identical), this is definitely the way to go.

Adding a new field, which I assume you won't modify in any meaningful way results in the same, just overcomplicates this. Same can be said for the other two approaches.

Please note that you can choose any value - you do not need to worry about possible hash code collisions between different types, so keep it simple.

decPL
  • 5,384
  • 1
  • 26
  • 36
  • 1
    I'd use this solution in distributed applications, when there's no single runtime environment between modules. – astef Aug 20 '14 at 12:09
2

If you want all instances to have the same hashcode without using constants you could also use the hashcode of the type:

    public class FirstRequestHeader
    {
        public override int GetHashCode()
        {
            return this.GetType().GetHashCode();
        }
    }
Uwe Hafner
  • 4,889
  • 2
  • 27
  • 44