Reputation: 2366
Why does this code not throw KeyNotFound exception?
var tempDic = new Dictionary<long, string>{{1,"a"},{2,"b"},{3,"c"}};
var realDic = new Dictionary<long, string>{{1,"Z"}};
Console.WriteLine("BEFORE:");
foreach (var item in tempDic)
{
Console.WriteLine($"TempDic: {item.Key} : {item.Value}");
}
foreach (var item in realDic)
{
Console.WriteLine($"realDic: {item.Key} : {item.Value}");
}
foreach (var key in tempDic.Keys)
{
if (!realDic.ContainsKey(key))
{
realDic[key] = tempDic[key];
}
}
Console.WriteLine();
Console.WriteLine("AFTER:");
foreach (var item in tempDic)
{
Console.WriteLine($"TempDic: {item.Key} : {item.Value}");
}
foreach (var item in realDic)
{
Console.WriteLine($"realDic: {item.Key} : {item.Value}");
}
Upvotes: 1
Views: 948
Reputation: 8227
In a Dictionary, realDic[key] = tempDic[key];
is another way to say realDic.Add(key, tempDic[key]);
. However, checking .NET source code, the Add(key, tempDic[key])
call will result in calling Insert
method.
That's the reason why a KeyNotFoundException
isn't thrown.
There's anyway a difference, explained in the MSDN documentation, regarding Add
method, "Remarks" section:
You can also use the
Item[TKey]
property to add new elements by setting the value of a key that does not exist in theDictionary<TKey,TValue>;
for example,myCollection[myKey] = myValue
(in Visual Basic,myCollection(myKey) = myValue
). However, if the specified key already exists in theDictionary<TKey,TValue>
, setting theItem[TKey]
property overwrites the old value. In contrast, the Add method throws an exception if a value with the specified key already exists.
Upvotes: 2
Reputation: 3235
Look at Dictionary<TKey,TValue>
source code.
Indexer looks this (remeber, you call the setter):
public TValue this[TKey key] {
get {
int i = FindEntry(key);
if (i >= 0) return entries[i].value;
ThrowHelper.ThrowKeyNotFoundException();
return default(TValue);
}
set {
Insert(key, value, false);
}
}
where Insert
is:
private void Insert(TKey key, TValue value, bool add) {
if( key == null ) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
if (buckets == null) Initialize(0);
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
int targetBucket = hashCode % buckets.Length;
#if FEATURE_RANDOMIZED_STRING_HASHING
int collisionCount = 0;
#endif
for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next) {
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) {
if (add) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
}
entries[i].value = value;
version++;
return;
}
#if FEATURE_RANDOMIZED_STRING_HASHING
collisionCount++;
#endif
}
int index;
if (freeCount > 0) {
index = freeList;
freeList = entries[index].next;
freeCount--;
}
else {
if (count == entries.Length)
{
Resize();
targetBucket = hashCode % buckets.Length;
}
index = count;
count++;
}
entries[index].hashCode = hashCode;
entries[index].next = buckets[targetBucket];
entries[index].key = key;
entries[index].value = value;
buckets[targetBucket] = index;
version++;
#if FEATURE_RANDOMIZED_STRING_HASHING
#if FEATURE_CORECLR
// In case we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing
// in this case will be EqualityComparer<string>.Default.
// Note, randomized string hashing is turned on by default on coreclr so EqualityComparer<string>.Default will
// be using randomized string hashing
if (collisionCount > HashHelpers.HashCollisionThreshold && comparer == NonRandomizedStringEqualityComparer.Default)
{
comparer = (IEqualityComparer<TKey>) EqualityComparer<string>.Default;
Resize(entries.Length, true);
}
#else
if(collisionCount > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(comparer))
{
comparer = (IEqualityComparer<TKey>) HashHelpers.GetRandomizedEqualityComparer(comparer);
Resize(entries.Length, true);
}
#endif // FEATURE_CORECLR
#endif
}
It gives the behavior you observe. If key
does not exists it is added and corresponding value
is set anyway.
Upvotes: 0