Dictionary where the key is a pair of integers

Maybe you should consider using a Tuple

var myDictionary = new Dictionary<Tuple<int,int>, List<string>>(); 
myDictionary.Add(new Tuple<int,int>(3, 3), "FirstItem"); 
myDictionary.Add(new Tuple<int,int>(5, 5), "SecondItem"); 

According to MSDN documentation, a Tuple objects Equals method will use the values of the two Tuple objects. This would result in one entry per Tuple in the outer dictionary and allow you to store a listing of the values per key.

Simply use a long as key and combine the two int keys

public class IntIntDict<T> : Dictionary<long, T>
    public void Add(int key1, int key2, T value)
        Add((((long)key1) << 32) + key2, value);

    //TODO: Overload other methods


C# 7 introduces the new ValueTuple Struct together with a simplified tuple syntax. These tuples come in handy for compound keys. You can declare your dictionary and add entries like this:

var myDictionary = new Dictionary<(int, int), string>();
myDictionary.Add((3, 3), "FirstItem"); 
myDictionary.Add((5, 5), "SecondItem");

and look up values like this

string result = myDictionary[(5, 5)];


if (myDictionary.TryGetValue((5, 7), out string result)) {
    //TODO: use result

For performance Dictionary requires a key that generates unique GetHashValue.

KeyValuePair is a value type and not recommended for a key.


If you call the derived type's GetHashCode method, the return value is not likely to be suitable for use as a key in a hash table. Additionally, if the value of one or more of those fields changes, the return value might become unsuitable for use as a key in a hash table. In either case, consider writing your own implementation of the GetHashCode method that more closely represents the concept of a hash code for the type.

Point is also a value value type and also not recommended for a key.
Tuple also generates a lot of duplicate GetHashCode and is not a good key.

The optimal key is one that generates unique keys.

Consider UInt16 i and UInt j as the two keys.
How can they be combined and generate unique hash?
Easy combine them into and UInt32.
UInt32 natively generates a perfect hash.

The alogorithm for packing two UInt16 into UInt32 is

(i * (UInt16.MaxValue + 1)) + j;

but it is even faster with

(UInt32)i << 16 | j;

myDictionary = new Dictionary<UInt32, string>();

With a perfect hash the Dictionary is O(1).
With a poor hash the Dictionary becomes O(n).