Click here to Skip to main content
14,666,665 members
Articles » General Programming » Algorithms & Recipes » Data Structures
Posted 27 Jan 2009


86 bookmarked

C# Multi-key Generic Dictionary

Rate this:
4.65 (38 votes)
Please Sign up or sign in to vote.
4.65 (38 votes)
23 May 2011CPOL
This is an example of a multi-key generic dictionary written in C#.


MultiKeyDictionary is a C# class that wraps and extends the Generic Dictionary object provided by Microsoft in .NET 2.0 and above. This allows a developer to create a generic dictionary of values and reference the value list through two keys instead of just the one provided by the Microsoft implementation of the Generic Dictionary<T,K>.


While writing a massive socket management application, I needed the ability to keep a list of sockets that I could identify by either their remote endpoint (IPEndPoint), or by a string that represented the internal ID of the socket. Thus was born the MultiKeyDictionary class.

Using the Code

Using the MultiKeyDictionary class is simple- instantiate the class, specifying the primary key, sub key, and value types in the generic constructor, then start adding your values and keys.

For this example, let's say I wanted to create a dictionary which stores the string representation of a number, i.e., 'Zero', 'One', 'Two', etc. Now, I want to access that list of items via an integer representation and a binary (in string format) representation.

// Instantiate class with a primary key (int),
// sub key (string) and value (string)

MultiKeyDictionary<int, string, string> dictionary = 
             new MultiKeyDictionary<int, string, string>();

Now you can add some values to the dictionary and associate the sub keys with the primary key.

// Adding 'Zero' to dictionary with primary int key of '0'
dictionary.Add(0, "Zero");

// Associating binary sub key of '0000' with primary int key of '0'
dictionary.Associate("0000", 0);

// Adding 'Two' to dictionary with primary int key of '2' and a binary sub key of '0010'
dictionary.Add(2, "0010", "Two");

Now you can easily pull values out of the MultiKeyDictionary in the same manner that you would a regular Dictionary:

string val = dictionary["0000"]; // val is set to 'Zero'
val = dictionary[2]; // val is set to 'Two'

Inside the MultiKeyDictionary

As stated earlier, the MultiKeyDictionary class is mainly a wrapper for a standard Generic Dictionary<TKey, TValue>. Internally, I keep a Dictionary<K,V> class (baseDictionary) that stores the primary key and value, as well as a Dictionary<L,K> class (subDictionary) that is created to store the sub-key to primary-key relationship.

/// <summary> 
/// Multi-Key Dictionary Class 
/// </summary> 
/// <typeparam name="K">Primary Key Type</typeparam>
/// <typeparam name="L">Sub Key Type</typeparam>
/// <typeparam name="V">Value Type</typeparam>
public class MultiKeyDictionary<K, L, V> 
    internal readonly Dictionary<K, V> baseDictionary = new Dictionary<K, V>();
    internal readonly Dictionary<L, K> subDictionary = new Dictionary<L, K>();
    internal readonly Dictionary<K, L> primaryToSubkeyMapping = new Dictionary<K, L>();

Notice above that I am also creating a dictionary that has the mappings for the sub and primary keys reversed, so I can quickly look up a sub key via a primary key. This is here so that when we remove items from the main dictionary, we can also remove the sub key mapping based on its hashed value as opposed to doing a costly for loop through the sub keys looking for the one with the corresponding primary key.

The following function shows how I associate a sub key with a key/value pair that has already been added to the underlying dictionary. While there is quite a bit of locking going on here, using the ReaderWriterLockSlim, it is essential that we maintain correctness when storing or reading data in this dictionary. On another note, you can associate a sub key with a primary key either by calling the three parameter Add function, or by calling the standard two parameter Add function and then calling Associate afterwards.

// Associate a subkey with a primary key after the primary key
// and value pair have been added to the underlying dictionary
public void Associate(L subKey, K primaryKey)

        if (!baseDictionary.ContainsKey(primaryKey))
            throw new KeyNotFoundException(string.Format(
              "The base dictionary does not contain the key '{0}'", primaryKey));

        if (primaryToSubkeyMapping.ContainsKey(primaryKey))
        // Remove the old mapping first

                if (subDictionary.ContainsKey(primaryToSubkeyMapping[primaryKey]))


        subDictionary[subKey] = primaryKey;
        primaryToSubkeyMapping[primaryKey] = subKey;

// Add a primary key and value pair and associate the given sub key
public void Add(K primaryKey, L subKey, V val)
    Add(primaryKey, val);

    Associate(subKey, primaryKey);

Upon removing an item from the MultiKeyDictionary, the class looks up the sub key in primaryToSubkeyMapping and removes it from subDictionary, then removes the mapping itself from primaryToSubkeyMapping. After that, the baseDictionary entry containing the primary key and value pair is removed.

public void Remove(K primaryKey)

        if (primaryToSubkeyMapping.ContainsKey(primaryKey))
            if (subDictionary.ContainsKey(primaryToSubkeyMapping[primaryKey]))



public void Remove(L subKey)




The above examples and more are contained in the code download.

Locking Strategy

The locking strategy that I am using is fairly basic. After experiencing some problems with locking on a base-dictionary object and a sub-dictionary object, and trying to maintain those relationships correctly so that I don't have any locking issues, I've simplified the way I am locking in order to maintain correctness inside the dictionary, with only a slight performance hit.

I am now using a ReaderWriterLockSlim for all synchronization. Using the built-in read/upgradable read/write locks is simpler, as far as allowing the dictionary and all of its sub-dictionaries to be treated, and locked, like a single object. Also, since the majority of the functions that are being called (from my application, anyway) are reads, the performance is much better than if I were to lock on an object or two, since that would preclude more than one thread entering a read operation at one time.

See Microsoft's documentation on the ReaderWriterLockSlim here:

Wrap Up

So, that's pretty much it. There are other functions available that allow you to clone the two key tables, as well as the values, somewhat like what the Generic Dictionary offers you. contains both the source code and a sample console application that should enlighten any user on the usage of MultiKeyDictionary. contains just the source code for MultiKeyDictionary.

Points of Interest

Since the application that I am using this MultiKeyDictionary in is massively multithreaded, the execution of all of the various functions are designed to be thread safe. However, since I am not omnipotent, I wouldn't be surprised if I missed something. Comments and constructive criticism are highly encouraged.


  • Revision 1 - Initial upload (Jan. 27, 2009) - There is a known bug in this revision, which I am not going to fix (long story- but basically, it would adversely affect my main application). This bug is that you cannot use the ContainsKey method if both key types are the same. The code can easily be fixed by just renaming the ContainsKey overload that looks for the sub key to ContainsSubKey.
  • Revision 1.5 - Modifications based on feedback (Jan. 27, 2009).
  • Revision 1.6 - Revised locking strategy based on some bugs found with the existing lock objects. Possible deadlock and race conditions resolved by swapping out the two lock objects for a ReaderWriterLockSlim. Performance takes a very small hit, but correctness is guaranteed.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Aron Weiler
Architect Becton Dickinson
United States United States
Check out my technical blog here: The Fyslexic Duck. You can find most of what I've put on CodeProject there, plus some additional technical articles.

Comments and Discussions

QuestionTryGetValue can become ambiguous Pin
Member 964282629-Sep-16 6:59
MemberMember 964282629-Sep-16 6:59 
QuestionFor each Loop Pin
Monika H Patel30-Aug-15 13:06
MemberMonika H Patel30-Aug-15 13:06 
GeneralMy vote of 1 Pin
Zachary Patten8-Aug-14 20:51
MemberZachary Patten8-Aug-14 20:51 
GeneralMessage Closed Pin
9-Aug-14 6:10
MemberAron Weiler9-Aug-14 6:10 
GeneralRe: My vote of 1 Pin
Zachary Patten9-Aug-14 6:24
MemberZachary Patten9-Aug-14 6:24 
GeneralMessage Closed Pin
11-Aug-14 8:18
MemberAron Weiler11-Aug-14 8:18 
GeneralRe: My vote of 1 Pin
Zachary Patten11-Aug-14 9:22
MemberZachary Patten11-Aug-14 9:22 
GeneralMessage Closed Pin
11-Aug-14 11:04
MemberAron Weiler11-Aug-14 11:04 
GeneralRe: My vote of 1 Pin
Zachary Patten11-Aug-14 12:53
MemberZachary Patten11-Aug-14 12:53 
Questionissue Pin
Mahalasa3-Sep-13 21:48
MemberMahalasa3-Sep-13 21:48 
QuestionVectors Pin
Terence Wallace29-Mar-13 8:08
MemberTerence Wallace29-Mar-13 8:08 
AnswerRe: Vectors Pin
Aron Weiler5-Apr-13 13:08
MemberAron Weiler5-Apr-13 13:08 
QuestionHow to change an item Pin
scorch-pt27-Aug-12 5:26
Memberscorch-pt27-Aug-12 5:26 
AnswerRe: How to change an item Pin
Aron Weiler27-Aug-12 7:58
MemberAron Weiler27-Aug-12 7:58 
QuestionThank you so much for this Pin
MadLittleMods20-Aug-12 10:15
MemberMadLittleMods20-Aug-12 10:15 
Questionval Pin
JamesTheBear23-Jul-12 22:50
MemberJamesTheBear23-Jul-12 22:50 
AnswerRe: val Pin
Aron Weiler26-Jul-12 13:28
MemberAron Weiler26-Jul-12 13:28 
GeneralMy vote of 5 Pin
Member 412624025-Aug-11 23:47
MemberMember 412624025-Aug-11 23:47 
GeneralMy vote of 5 Pin
prromap25-Jun-11 22:42
Memberprromap25-Jun-11 22:42 
GeneralMy vote of 5 Pin
Jay R. Wren31-May-11 6:23
MemberJay R. Wren31-May-11 6:23 
GeneralVote of 5 Pin
arisoqq24-May-11 10:16
Memberarisoqq24-May-11 10:16 
QuestionI'm probably missing the point but... Pin
Dave Sexton24-May-11 2:23
MemberDave Sexton24-May-11 2:23 
AnswerRe: I'm probably missing the point but... Pin
Aron Weiler24-May-11 8:19
MemberAron Weiler24-May-11 8:19 
GeneralRe: I'm probably missing the point but... Pin
Aron Weiler24-May-11 8:24
MemberAron Weiler24-May-11 8:24 
GeneralGood but ... Pin
Israel Cris Valenzuela23-May-11 17:17
MemberIsrael Cris Valenzuela23-May-11 17:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.