ContainsKey() does not find key

A

Andrus

I need to create object cache in memory.

Each object id can be composed from several string, integer and decimal type
values.

ContainsKey does not find existing key in this case.

How to force ContainsKey to compare object contents and find key?

Should i use Binaryformatter to serialize CacheKey, overload CacheKey ==
operator or any other idea ?

Andrus.


To reproduce,

run the code.

Observed:

Key not found

Expected:

Key found


using System.Collections.Generic;

class test {
struct CacheKey {
object Key;
public CacheKey(object key) {
Key = key;
}
}

static Dictionary<CacheKey, object> Cache =
new Dictionary<CacheKey, object>();

static void Main() {
CacheKey key = new CacheKey(new object[] { "test1", 1 });
Cache.Add(key, "");
CacheKey key2 = new CacheKey(new object[] { "test1", 1 });
if (!Cache.ContainsKey(key2))
System.Windows.Forms.MessageBox.Show("Key not found");
else
System.Windows.Forms.MessageBox.Show("Key found");
}
}
 
A

Alberto Poblacion

Andrus said:
How to force ContainsKey to compare object contents and find key?

Should i use Binaryformatter to serialize CacheKey, overload CacheKey ==
operator or any other idea ?

In your class CacheKey, you have to override the method Equals that you
inherit from System.Object. Otherwise, Equals (which is being called by
ContainsKey to find your key) defaults to ReferenceEquals, which will return
false for the two objects that you use in your example since they are
different references.

When overriding Equals, you should also override the GetHashCode method
so that it returns the same code for two objects that are considered equal.
 
J

Jon Skeet [C# MVP]

I need to create object cache in memory.

Each object id can be composed from several string, integer and decimal type
values.

ContainsKey does not find existing key in this case.

How to force ContainsKey to compare object contents and find key?

By overriding Equals and GetHashCode, basically. If you implement
IEquatable<CacheKey> you'll be saved the boxing penalty too (IIRC).

Out of interest, do you have any particular reason to make CacheKey a
struct rather than a class?

Jon
 
A

Andrus

How to force ContainsKey to compare object contents and find key?
By overriding Equals and GetHashCode, basically.

This seems to complicated (I'm new to C#). Where to find sample of this ?

Or is it simpler to override ToString() method and use string as distionary
key ?

If you implement
IEquatable<CacheKey> you'll be saved the boxing penalty too (IIRC).

I don't understand this.
Out of interest, do you have any particular reason to make CacheKey a
struct rather than a class?

I my real application I hold also object type in this structure:

struct CacheKey {
Type ObjectType;
object ID;

public CacheKey(Type objectType, object id) {
ObjectType = objectType;
ID = id;
}
}

ObjectType holds business object type : typeof(Customer) etc.
Otherwise different objects may suddenly have same keys.

Is this best approcach ?

Andrus.
 
A

Andrus

Alberto Poblacion said:
In your class CacheKey, you have to override the method Equals that you
inherit from System.Object. Otherwise, Equals (which is being called by
ContainsKey to find your key) defaults to ReferenceEquals, which will
return false for the two objects that you use in your example since they
are different references.

Key is object.
Usually it contains object (string, integer) array but class designer can
implement it differently.

How to override object Equals method in this case ?

Or should I require that object key contains object array always ?
When overriding Equals, you should also override the GetHashCode method
so that it returns the same code for two objects that are considered
equal.

This seems to be sophisticated.
Maybe it is simpler use string key and to serialize object to strin using
BinaryFormatter ?

Will Equals work propery with string keys ?

Andrus.
 
J

Jon Skeet [C# MVP]

Andrus said:
This seems to complicated (I'm new to C#).

It's not particularly complicated at all, and it's absolutely necessary
- you have to be able to tell the platform under what circumstances two
instances are "equal".
Where to find sample of this ?

The documentation for Equals and GetHashCode is pretty good.
Or is it simpler to override ToString() method and use string as distionary
key ?

No, that's a bad idea. ToString() should be used for formatting data
into a human readable form - it's not meant for keys.
I don't understand this.

Boxing it too large a topic to quickly cover in a newsgroup post - I
suggest you look it up in a C# book.
I my real application I hold also object type in this structure:

struct CacheKey {
Type ObjectType;
object ID;

public CacheKey(Type objectType, object id) {
ObjectType = objectType;
ID = id;
}
}

ObjectType holds business object type : typeof(Customer) etc.
Otherwise different objects may suddenly have same keys.

Is this best approcach ?

There's nothing wrong with that in general (although by convention the
fields would be in camel case), but you still haven't given any reasons
for using a value type (a struct) instead of a reference type (a
class). Most of the time you want to create classes.
 
J

Jon Skeet [C# MVP]

add this to cache object Cache.Add(key2, 1);

But the point was to find that key2 was already in the cache, by
equality. The reason it's not working as intended is that the equality
and hashcode operations haven't been set up.

Jon
 
A

Andrus

How to force ContainsKey to compare object contents and find key?
It's not particularly complicated at all, and it's absolutely necessary
- you have to be able to tell the platform under what circumstances two
instances are "equal".

I read nhibernate and activerecord docs.

Would'nt it more reasonable to useActiveRecord with NHibernate ?
NHibernate seems to have built-in object cache so I do'nt need to create it
from scratch.
There's nothing wrong with that in general (although by convention the
fields would be in camel case), but you still haven't given any reasons
for using a value type (a struct) instead of a reference type (a
class). Most of the time you want to create classes.

Yes I ffirst create class.
However class was not working even when structure contains scalar types.
After that I canged cache structure to structure. In this case I found that
my cache works when structure contains scalars, not reference types.

Andrus.
 
J

Jon Skeet [C# MVP]

Andrus said:
I read nhibernate and activerecord docs.

Would'nt it more reasonable to useActiveRecord with NHibernate ?
NHibernate seems to have built-in object cache so I do'nt need to create it
from scratch.

You could certainly use someone else's cache, but you ought to
understand what's going on here or you may well find similar
problematic results using other caches.
Yes I ffirst create class.
However class was not working even when structure contains scalar types.
After that I canged cache structure to structure. In this case I found that
my cache works when structure contains scalars, not reference types.

That's due to the way that ValueType.Equals is implemented, but it
certainly shouldn't be the deciding factor as to whether you use a
value type or a reference type.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top