Pregunta

I am interested in your opinion regarding an issue that came up while implementing a small library for writing integration/system tests in C#. The library is composed of 2 parts: a test Authoring API and a test Runtime API. The test writer uses the authoring API to build test plan prototypes. The runtime can take the a prototype and create a runtime representation of the prototype and execute it.

To be able to test a class named RuntimeTestPlanBuilder I had to override the Equals method on the entire runtime object model classes. This led to a situation where I should override the GetHashCode method too. Implementing GetHashCode for some of the runtime object model classes was easy. Since some classes on the runtime model are containers implementing GetHashCode for them was tough and even impossible (since the runtime can add items to a container the hash code cannot be computed in O(1), for example).

As a result I found myself wondering how to prevent from users of the containers to put them on a dictionary as keys. The solution I chose is to override the GetHashCode method and throw an exception. I am not satisfied with this solution since it will only fail on runtime. Plus, I kind of wonder why the .Net Framework designers chose to put the GetHashCode method on the Object class. I think that this decision led to my situation since I cannot mark my containers as not legitimate dictionary keys in a fine way.

A better decision (for the .Net Framework designers) might have been to define an interface named IHashable with 2 methods: Equals and GetHashCode and enforce the interface with a constraint over the generic dictionary Item type. This way we can enforce in compile time that keys implement the Equals and GetHashCode. Also, it is easy to understand that classes that do not implement this interface are not supposed to be used as dictionary keys.

My question is: Can you propose a better solution (one that do not throw exceptions from GetHashCode) to speciy that some class's instances should not be used as dictionary keys?

Thanks

¿Fue útil?

Solución

An Object is supposed to implement five methods and GetHashCode is one of them.
Cannot be sure the only current or future use of GetHashCode is for Dictionary key.

The only rule is two equal objects must have the same GetHashCode.
But the same GetHashCode does not mean two objects are equal.

You could just use a constant (1) for GetHashCode or the number of items in the container.
Or see this answer from Skeet

is-it-possible-to-combine-hash-codes-for-private-members-to-generate-a-new-hash

Otros consejos

Generally, two objects should only report themselves as equal if they are and always will be equivalent; mutable objects, including mutable collections, should thus generally only be considered equal to each other. Immutable collections may implement GetHashCode efficiently by caching the GetHashCode() values of their members; construction is O(N), and whether the hash code is computed on object construction or when it's first requested, the total lifetime cost of computing it will be at most O(N).

There's no reason any class should ever have difficulty implementing a hash code. If a class uses reference equality, the default GetHashCode is perfect. If it uses a mutable definition of equality, the GetHashCode() contract may be complied with by simply returning a constant. Trying to put more than a few instances into a dictionary may yield poor performance, but it's possible that someone might have a use for a dictionary with e.g. a dozen items in it, and performance would be just fine with a dictionary that size.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top