Thanks for the comments. I think the main reason I wrote
it the way I did was for clarity's sake.
Insofar as using
+=, if I'm not mistaken, because
the indexer (TValue this[TKey]) is a
property with
both a
set() and a
get() method, using '+=' on it is
roughly equivalent to:
dict["key"] = dict["key"] + 1; // 2 lookups :
or expressing it as calls to the set() and get() methods of
the indexer:
this_Set( "key", this_Get( "key" ) + 1 );
And of course, both the set() and get() methods of the
indexer property must perform a lookup.
The += operator is actually a compiler macro for the +
and the = operators, and + knows nothing about the
context, and the compiler can't be doing any kind of
hocus-pocus there either, since there's nothing in the
Dictionary<TKey, TValue> class that would allow that.
So, my guess is that doing it that way (using += after
a call to ContainsKey()) would require
3 lookups (two
through the indexer to get/set the value and the third
being done by ContainsKey).
In case it may be helpful to anyone, the way I've been
dealing with this issue in cases where values of existing
dictionary entries are updated at a high frequency, is by
storing value types in a reference type 'container' class,
like this:
class Ref<T> where T: struct
{
public Ref( T value )
{
this.Value = value;
}
public T Value {get;set;}
public override bool Equals( Ref<T> other )
{
return other != null && other.Value == this.Value;
}
public override int GetHashCode()
{
return this.Value.GetHashCode();
}
public static implicit operator T( Ref<T> val )
{
return val.Value;
}
public static implicit operator Ref<T>( T val )
{
return new Ref<T>( val );
}
}
I can use
Ref<int> as the TValue in a Dictionary where
the actual value being stored is an int, like so:
Dictionary<string, Ref<int>> items = new Dictionary<string, Ref<int>>();
With that, mutating the value of an existing entry
requires only
one lookup:
Ref<int> item = null;
if( items.TryGetValue( "key", out item ) )
item.Value++;
else
items.Add( "key", 1 ); // implicit cast to Ref<int>
And, the implicit cast operator also makes the
container transparent when retrieving values
through the indexer:
int value = dict["key"]; // implicit cast to int
I checked the disassembly of some code like this
and was able to confirm that the jitter inlines the
calls to the implicit cast operator methods which
eliminates the method call overhead, so it's not
terribly expensive to take this route when there's
not a massive number of items in a dictionary, but
I'd still rather not need it at all.
Not to distract from your proposal, but wouldn't you typically write something like this? Which still requires two lookups, of course.
public static void IncrementValue<TKey>(Dictionary<TKey, int> dict, TKey key)
{
if (dict.ContainsKey(key))
dict[key] += 1;
else
dict.Add(key, 1);
}