Click here to Skip to main content
13,800,935 members
Click here to Skip to main content
Add your own
alternative version


15 bookmarked
Posted 11 Mar 2009
Licenced CPOL

Trictionary - Multi-Value Dictionary

, 11 Mar 2009
Rate this:
Please Sign up or sign in to vote.
A wrapper around the Dictionary that returns two values instead of one.


The Dictionary<TKey, TValue> object is a very powerful collection in the .NET framework. It's a type-safe implementation of the Hashtable that was prevalent in .NET 1.1. Recently, I needed something extra - instead of returning a single value for a given key, I needed two values. I found the solution - instead of a Dictionary, I created a Trictionary.


A Dictionary is pretty straight-forward to use. For this example, we have a Dictionary with a key of type int and a value of type string. There are two ways to add keys and values:

Dictionary<int, string> dict = new Dictionary<int, string>();

// Option 1:
dict.Add(1, "A");

// Option 2:
dict[2] = "B";

The two options are slightly different, but generally produce the same results. Option 1 will throw an exception if the key already exists in the dictionary. Option 2 will not throw an exception in this scenario, but will simply overwrite the value found.

Retrieving values is pretty straightforward as well:

string s = dict[1]; // returns "A"

If the Dictionary doesn't contain the key 1, then a KeyNotFoundException will be thrown. To avoid this, you can call the ContainsKey method first to ensure that the key exists.

Now, suppose you need the dictionary to return two objects instead of just one. For example, suppose you want your key to be an int, and you want to return a string and decimal. There are two simple ways of accomplishing this:

// Option 1: Create a container class
class Container {
    public string S { get; set; }
    public decimal D { get; set; }
Dictionary<int, Container> dict;

// Option 2: Use a KeyValuePair<TKey, TValue> as the value
Dictionary<int, KeyValuePair<string, decimal>> dict;

With option 1, you'd need to define a container for each type you need this functionality in. With option 2, the code tends to get kind of messy. I wanted a better solution.

Using the code

I decided to build the Trictionary class as a wrapper around the Dictionary, to utilize the features that make the Dictionary so powerful. The value of the dictionary is actually a type called DualObjectContainer<TValue1, TValue2>. It's a pretty simple class, with just two values. I overrode ToString() to show the values, and implemented IEquatable so two containers can be compared.

public class DualObjectContainer<TValue1, TValue2> 
    : IEquatable<DualObjectContainer<TValue1, TValue2>>
    public DualObjectContainer(TValue1 value1, TValue2 value2) {
        Value1 = value1;
        Value2 = value2;

    public TValue1 Value1 { get; set; }
    public TValue2 Value2 { get; set; }

    public bool Equals(DualObjectContainer<TValue1, TValue2> other) {
        return ((EqualityComparer<TValue1>.Default.Equals(Value1, other.Value1)) 
            && (EqualityComparer<TValue2>.Default.Equals(Value2, other.Value2)));

    public override string ToString() {
        return string.Format("[{0}, {1}]", Value1, Value2);

The Trictionary class contains a private Dictionary, and many of the methods and properties are simply wrappers around this Dictionary.

public class Trictionary<TKey, TValue1, TValue2> 
    : ITrictionarylt;TKey, TValue1, TValue2>, ITrictionary, 
      ISerializable, IDeserializationCallback
    private readonly Dictionary<TKey, DualObjectContainer<TValue1, TValue2>> _dictionary;

    public void Clear()

    public bool ContainsKey(TKey key)
        return _dictionary.ContainsKey(key);

    public int Count
        get { return _dictionary.Count; }
    // etc.

In order to add and retrieve values from the Trictionary, I had to do something a little different. Just like the Dictionary, there are two ways to add keys/values. First, you can use the Add method, passing in the key and both values. You can also use the indexer, but to do that, you need to pass in a DualObjectContainer with the two values. There are also two ways to retrieve values - the Get method and the indexer.

public void Add(TKey key, TValue1 value1, TValue2 value2) {
       _dictionary.Add(key, new DualObjectContainer<TValue1, TValue2>(value1, value2));

public void Get(TKey key, out TValue1 value1, out TValue2 value2) {
    DualObjectContainer<TValue1, TValue2> container = _dictionary[key];
    value1 = container.Value1;
    value2 = container.Value2;

public DualObjectContainer<TValue1, TValue2> this[TKey key] {
    get { return _dictionary[key]; }
    set { _dictionary[key] = value; }

Here is how you call the code:

Trictionary<int, string, double> trict = new Trictionary<int, string, double>();

// Option 1:
trict.Add(1, "A", 1.1);

// Option 2:
trict[2] = new DualObjectContainer<string,double>("B", 2.2);

// Option 1:
string s;
double d;
trict.Get(2, out s, out d);

// Option 2:
DualObjectContainer<string, double> container = trict[1];

The Trictionary is more than just a simple wrapper. In order to expose the same features as a Dictionary, I had to define the ITrictionary interfaces, both generic and non-generic, which required a significant amount of additional code. This code really isn't important to this presentation, but it is there to make the Trictionary more useful. For example, you can access the keys and values, and all of the different constructors that are available in the Dictionary.


This class can provide a useful benefit in some circumstances. One scenario where I found it useful was with a data object: the ID was the key, the object itself was Value1, and Value2 represented additional information about the object. I hope you find a situation where the download will be of some value as well. Please feel free to leave a comment letting me know if you've found this technique helpful in your projects.

The attached ZIP file contains a Visual Studio 2008 solution, with projects designed to compile under the .NET 3.5 Framework. It contains a Trictionary project, which contains all of the necessary code files, and a Trictionary.TestFixture project, which contains various Unit Tests designed to run in NUnit.


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


About the Author

Joe Enos
Software Developer Desert Schools Federal Credit Union
United States United States
Joe Enos is a software engineer in Phoenix, Arizona, with 8 years of .NET experience. Currently working as a software developer on a medium-sized team in Phoenix.

You may also be interested in...

Comments and Discussions

Generalin 4.0, I guess its just Dictionary&lt;tk,tuple&gt;&lt;t1,t2&gt;&gt; Pin
Jay R. Wren17-Mar-09 9:14
memberJay R. Wren17-Mar-09 9:14 
GeneralRe: in 4.0, I guess its just Dictionary&lt;tk,tuple&gt;&lt;t1,t2&gt;&gt; Pin
Jay R. Wren17-Mar-09 9:27
memberJay R. Wren17-Mar-09 9:27 
GeneralRe: in 4.0, I guess its just Dictionary&lt;tk,tuple&gt;&lt;t1,t2&gt;&gt; Pin
Joe Enos17-Mar-09 10:58
memberJoe Enos17-Mar-09 10:58 
GeneralMy vote of 5 Pin
Nish Nishant11-Mar-09 17:16
sitebuilderNish Nishant11-Mar-09 17:16 
GeneralRe: My vote of 5 Pin
Joe Enos11-Mar-09 17:58
memberJoe Enos11-Mar-09 17:58 
GeneralJust An Idea Pin
Shani Natav11-Mar-09 11:57
memberShani Natav11-Mar-09 11:57 
GeneralRe: Just An Idea Pin
Joe Enos11-Mar-09 12:18
memberJoe Enos11-Mar-09 12:18 
GeneralRe: Just An Idea Pin
Joe Enos11-Mar-09 12:25
memberJoe Enos11-Mar-09 12:25 
GeneralRe: Just An Idea Pin
Shani Natav11-Mar-09 12:38
memberShani Natav11-Mar-09 12:38 
GeneralRe: Just An Idea Pin
Joe Enos11-Mar-09 12:51
memberJoe Enos11-Mar-09 12:51 
GeneralAnother way to do this Pin
Aron Weiler11-Mar-09 10:23
memberAron Weiler11-Mar-09 10:23 
GeneralRe: Another way to do this Pin
Joe Enos11-Mar-09 10:56
memberJoe Enos11-Mar-09 10:56 
GeneralMy vote of 5 Pin
BigTuna11-Mar-09 5:51
memberBigTuna11-Mar-09 5:51 
GeneralRe: My vote of 5 Pin
Joe Enos11-Mar-09 5:58
memberJoe Enos11-Mar-09 5:58 
GeneralA better implmentation is MultiDictionary Pin
Tawani Anyangwe11-Mar-09 4:17
memberTawani Anyangwe11-Mar-09 4:17 
GeneralRe: A better implmentation is MultiDictionary Pin
Joe Enos11-Mar-09 5:13
memberJoe Enos11-Mar-09 5:13 
GeneralI say "good job" Pin
Marc Clifton11-Mar-09 4:17
protectorMarc Clifton11-Mar-09 4:17 
GeneralRe: I say "good job" Pin
Joe Enos11-Mar-09 5:32
memberJoe Enos11-Mar-09 5:32 
GeneralMy vote of 1 Pin
thehawk_220011-Mar-09 4:07
memberthehawk_220011-Mar-09 4:07 
GeneralRe: My vote of 1 Pin
Joe Enos11-Mar-09 5:19
memberJoe Enos11-Mar-09 5:19 
GeneralMy vote of 1 Pin
Michael B. Hansen11-Mar-09 3:18
memberMichael B. Hansen11-Mar-09 3:18 
GeneralRe: My vote of 1 Pin
Joe Enos11-Mar-09 5:17
memberJoe Enos11-Mar-09 5:17 
QuestionStruct? Pin
burrows.stephen11-Mar-09 3:06
memberburrows.stephen11-Mar-09 3:06 
AnswerRe: Struct? Pin
Joe Enos11-Mar-09 5:15
memberJoe Enos11-Mar-09 5:15 

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.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web04 | 2.8.181215.1 | Last Updated 11 Mar 2009
Article Copyright 2009 by Joe Enos
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid