问题
The program was working with this implementation:
class Instrument
{
public string ClassCode { get; set; }
public string Ticker { get; set; }
public override string ToString()
{
return " ClassCode: " + ClassCode + " Ticker: " + Ticker + '.';
}
}
But because I need to use Instrument in Dictionary I've decided to implement equals/hashcode:
class Instrument
{
public string ClassCode { get; set; }
public string Ticker { get; set; }
public override string ToString()
{
return " ClassCode: " + ClassCode + " Ticker: " + Ticker + '.';
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
Instrument instrument = obj as Instrument;
if (instrument == null)
return false;
return ((ClassCode.Equals(instrument.ClassCode)) && (Ticker.Equals(instrument.Ticker));
}
public override int GetHashCode()
{
int hash = 13;
hash = (hash * 7) + ClassCode.GetHashCode();
hash = (hash * 7) + Ticker.GetHashCode();
return hash;
}
}
Now the program has stopped working. In such or similar places I receive "KeyNotFoundException":
if (cache.Keys.Any(instrument => instrument.Ticker == newTicker && instrument.ClassCode == newClassCode))
Is it possible that some pieces of the code assume that equals and hashcode IS NOT implemented? Or probably I just implemented them wrong? Sorry I'm not familiar with such advanced features in C# as the last piece of code and don't know how it is connected with equals or hashCode.
回答1:
Your HashCode and Equals methods should depend only on immutable properties - your implementation uses ClassCode and Ticker which both have setters and are therefore mutable.
回答2:
First, instead of using cache.Keys.Any
you can just use ContainsKey.
bool contains = cache.ContainsKey(
new Instrument { Ticker = newTicker, ClassCode = newClassCode });
The first iterate over the whole keys list - O(n), while the second uses Dictionary's built in hash table implementation - O(1).
Second, check for null reference in your implementation:
public override bool Equals(object obj)
{
if (obj == null)
return false;
Instrument instrument = obj as Instrument;
if (instrument == null)
return false;
// 1. string.Equals can handle null references.
// 2. object.ReferenceEquals for better preformances when it's the same object
return (object.ReferenceEquals(this, instrument)) ||
(string.Equals(ClassCode, instrument.ClassCode) &&
string.Equals(Ticker, instrument.Ticker));
}
public override int GetHashCode()
{
int hash = 13;
if (ClassCode != null)
hash = (hash * 7) + ClassCode.GetHashCode();
if (Ticker!= null)
hash = (hash * 7) + Ticker.GetHashCode();
return hash;
}
Other than that, I can't see a problem.
回答3:
But because I need to use Instrument in Dictionary I've decided to implement equals/hashcode
That's the wrong reason. Your class already has implementations for Equality and GetHashCode that are suitable, efficient and tested for use in a Dictionary.
Have I implemented Equals()/GetHashCode() correctly?
No. You are missing an overload for ==
to start with. And it will only be reliable when you make Instrument immutable.
Your best course of action is not to override any of those members.
Also see this MSDN advice. Notice the "guarantees of Equals" and
Overriding operator == in non-immutable types is not recommended.
来源:https://stackoverflow.com/questions/5700099/have-i-implemented-equals-gethashcode-correctly