Shawn
Shawn

Reputation: 2366

c# Dictionary not throwing KeyNotFound exception?

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}");
        }

Output

Upvotes: 1

Views: 948

Answers (2)

Alberto Solano
Alberto Solano

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 the Dictionary<TKey,TValue>; for example, myCollection[myKey] = myValue (in Visual Basic, myCollection(myKey) = myValue). However, if the specified key already exists in the Dictionary<TKey,TValue>, setting the Item[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

CSDev
CSDev

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

Related Questions