Reputation: 1587
To be more precise, I have something like this:
Dictionary<KeyValuePair<MyState, MyAction>, float> Dict =
new Dictionary<KeyValuePair<MyState, MyAction>, float>();
Later on, I have a self defined KeyValuePair object;
KeyValuePair<MyState, MyAction> kvp =
new KeyValuePair<MyState, MyAction>(new MyState(...), new MyAction(...));
My question is: if the kvp state and actions have the exactly same values with a pair in Dict (in Dict exists a KeyValuePair with an identical MyState with the kvp MyAction and also, the MyAction from Dict has the exactly same values with the MyAction in kvp). The only difference are that the reference is different.
Long story short, having 2 KeyValuePairs objects, both with the same value (different reference), how can I get the float Value from Dict, without having to iterate the whole dictionary just to manually compare each key.key and key.value just to see if the key is actually the same:
foreach(var StAct in Dict)
if(StAct.Key.Key.Equals(kvp.Key) &&
StAct.Key.Value.Equals(kvp.value))
//where StAct.Key.Key is the MyState object and StAct.Key.Value is the MyAction object
{
MessageBox.Show(StAct.Value + "");
break;
}
Upvotes: 1
Views: 102
Reputation: 1485
Posting a detailed example :
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public override bool Equals(object obj)
{
return this.Id==((Foo)obj).Id
&& this.Name==((Foo)obj).Name;
}
public override int GetHashCode()
{
return Id.GetHashCode() + Name.ToLower().GetHashCode();
}
}
public class Bar
{
public int SomeOtherId { get; set; }
public string SomeOtherName { get; set; }
public override bool Equals(object obj)
{
return this.SomeOtherId == ((Bar)obj).SomeOtherId
&& this.SomeOtherName == ((Bar)obj).SomeOtherName;
}
public override int GetHashCode()
{
return SomeOtherId.GetHashCode() + SomeOtherName.ToLower().GetHashCode();
}
}
//Usage
var dict = new Dictionary<KeyValuePair<Foo, Bar>, float>();
dict.Add(new KeyValuePair<Foo, Bar>(new Foo { Id = 1, Name = "Foo" }, new Bar {SomeOtherId = 1, SomeOtherName = "Bar"}), 10);
Console.WriteLine(dict[new KeyValuePair<Foo, Bar>(new Foo { Id = 1, Name = "Foo" }, new Bar {SomeOtherId = 1, SomeOtherName = "Bar"})]);
The answer is in accordance with the Title of the post. However, it still does not follow, if there are same keys in a dictionary(although different reference) because when you try to add such key(after you have applied the Equals
& GetHashCode
), it wont allow you to insert these same keys.
Upvotes: 0
Reputation: 3821
You need to implement GetHashCode()
and Equals()
on your KeyValuePair<>
-class. These are the methods Dictionary<>
uses to find a key.
The rule is that you have to override GetHashCode()
if you override Equals()
, because you have to ensure that two "equal" objects will always produce the same hash code. This is absolutely required for Dictionary<>
to work correctly.
Dictionary<>
first looks for the right key by searching for its hash code, and only then it uses Equals()
to make sure it really found the right one.
However, you can have the same hash code for objects which are not equal. It is perfectly legal to implement GetHashCode()
by always returning 1
, for example, because it satisfies the rule that equal objects have the same hash code. However, it will make your Dictionary<>
operations terribly slow - basically, the Dictionary<>
will now have to search through all of its entries to find the right key, just as if it was a simple list.
What you probably want for your KeyValuePair<>
is that two KeyValuePair<>
objects are equal if both their keys and their values are equal in turn. You can implement that like this:
public class KeyValuePair<K, V>
{
private K m_key;
private V m_value;
// [...] existing implementation left out
public override bool Equals(object obj)
{
KeyValuePair<K, V> other = obj as KeyValuePair<K, V>;
if(other == null)
return false;
return object.Equals(this.m_key, other.m_key) && object.Equals(this.m_value, other.m_value);
}
public override int GetHashCode()
{
int hashCode = 0;
if(m_key != null)
hashCode += m_key.GetHashCode();
if(m_value != null)
hashCode = hashCode * 31 + m_value.GetHashCode();
return hashCode;
}
}
Note that this requires that the K and V also implement Equals()
and GetHashCode()
in a way that makes sense for you.
Upvotes: 0
Reputation: 12184
You need to ensure that inside your MyState object, which serves as the key to the Dictionary, you have correctly overridden both the Equals() and GetHashCode() methods.
As you have noted, the default behaviour for classes is to check reference equality so if you want some different behaviour, you must provide that yourself.
public class MyState
{
public override bool Equals(object obj)
{
// your equality implementation goes here
}
public override int GetHashCode()
{
// your hashcode implementation goes here
}
}
Creating a well-behaved GetHashCode() method isn't necessarily trivial but you can find some good advice about how to do that in this answer: https://stackoverflow.com/a/371348/5438433
Once you have done that, you can simply write:
if(dict.ContainsKey(kvp.Key)) {.....}
Upvotes: 2