Reputation: 10163
In C#, the Dictionary
class takes a single key and maps it to a single value. I'm looking for something similar, where I can pass in an ordered tuple and get a single value -- without wrapping it in a class.
Here's a pretty hypothetical example of what I want (with buttons, instead of some 2D map tile). Currently, I can do this:
Dictionary<int, Button> buttons = new Dictionary<int, Button>();
If I want to use each button's coordinates as the key, I can do this:
Dictionary<Point, Button> buttons = new Dictionary<Point, Button>();
buttons[new Point(b.X, b.Y)] = b;
What I would like to do is this
Dictionary<int, int, Button> buttons = new Dictionary<int, int, Button>();
buttons[b.X, b.Y] = b;
Again, it's a trivial case with a known work-around. But I just find it annoying that I have to create a new placeholder class (struct?) with each set of parameters that I want to use as a key.
Is this somehow possible?
Upvotes: 1
Views: 3729
Reputation: 117029
This is a situation that extension methods may help.
I've written a bunch of dictionary extension methods to allow functions like GetOrAdd
and GetOrDefault
that work on standard dictionaries and these help reduce all of the noise in checking if an item already exists in dictionary.
Likewise, you can extend a nested dictionary with extension methods.
So, perhaps you could write code like this:
var buttons = new Dictionary<int, Dictionary<int, Button>>();
buttons.Add(b.X, b.Y, b);
buttons.AddOrReplace(b1.X, b1.Y, b1);
My Add
method looks like this:
public static void Add<K1, K2, V>(
this IDictionary<K1, Dictionary<K2, V>> @this,
K1 key1,
K2 key2,
V value)
{
if (@this == null) { throw new ArgumentNullException("@this"); }
if (key1 == null) { throw new ArgumentNullException("key1"); }
if (key2 == null) { throw new ArgumentNullException("key2"); }
if ([email protected](key1))
{
@this[key1] = new Dictionary<K2, V>();
}
@this[key1][key2] = value;
}
This kind of approach is nice because you are still working with standard .NET dictionaries, but getting the functionality you're after with only a relatively minor syntax difference.
An alternative approach is to define your own dictionary-like class. Something like this:
public class TupleDictionary<K1, K2, V>
{
public void Add(K1 key1, K2 key2, V value);
public bool ContainsKey(K1 key1, K2 key2);
public bool Remove(K1 key1, K2 key2);
public Dictionary<K2, V> this[K1 key1] { get; }
}
And then, because the this
indexed property would "get or add" the inner dictionaries, you can write your code like this:
var td = new TupleDictionary<int, int, Button>();
td[b.X][b.Y] = b;
Syntactically it looks the same as your example code, but you'd need to write a fair amount of code just to get this specific syntax.
I think the extension method approach using nested dictionaries is the way to go.
Upvotes: 2
Reputation: 57899
public class TupleDictionary<T1, T2, TValue> : Dictionary<Tuple<T1,T2>,TValue>
{
public TValue this[T1 t1, T2 t2]
{
get { return this[new Tuple<T1, T2>(t1, t2)]; }
set { this[new Tuple<T1, T2>(t1,t2)] = value; }
}
public void Add(T1 t1, T2 t2, TValue value)
{
Add(new Tuple<T1, T2>(t1, t2), value);
}
public void Remove(T1 t1, T2 t2)
{
Remove(new Tuple<T1, T2>(t1, t2));
}
public bool ContainsKey(T1 t1, T2 t2)
{
return ContainsKey(new Tuple<T1, T2>(t1, t2));
}
public bool TryGetValue(T1 t1, T2 t2, out TValue value)
{
return TryGetValue(new Tuple<T1, T2>(t1, t2), out value);
}
}
Upvotes: 5
Reputation: 2853
In .Net 4 you can use a tuple as the dictionary key
Dictionary<Tuple<int, int>, Button> buttons = new Dictionary<Tuple<int, int>, Button>();
buttons[new Tuple<int,int>(b.X, b.Y)] = b;
Upvotes: 2