Simply Me
Simply Me

Reputation: 1587

How can I get the value without giving as key the exact reference?

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

Answers (3)

blogbydev
blogbydev

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

Medo42
Medo42

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

TheInnerLight
TheInnerLight

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

Related Questions