Dictionary.TryGetValue and Anonymous Types
One of the methods I find to be the most useful in .Net is the method Dictionary<TKey,TValue>.TryGetValue. This method is a nice compromise between performance, explicit return vs. exception, and a being verbal about the chance of failure. It returns false on failure and uses an out parameter to return the actual requested value. This leads to the following elegant pattern
Student value;
if (map.TryGetValue("SomeKey", out value)) {
// Value is present
}
else {
// Value is not present
}
This works great right up until have a Dictionary of anonymous types. The TryGetValue pattern functions on out parameters which do not work well with type inference in C# and hence anonymous types. Type inference requires that the value be declared with a corresponding initialization expression. But any out call forces the declaration of the type and the initialization expression to be different statements breaking any chance of type inference.
For example take the following code which builds up a Dictionary object where the value is typed to be an anonymous type [1]
var query = from it in GetStudents()
where it.LastName.StartsWith(lastNamePrefix)
select new { FirsName = it.FirstName, LastName = it.LastName };
// ...
var map = query.ToDictionary(x => x.FirsName);
// How to use TryGetValue?
WhatDoIPutHere??? value;
if ( map.TryGetValue("Joe", out value) {
// ...
}
To fix this problem we need to write a wrapper around TryGetValue which allows us to combine both the presence or absence of the entry in the Dictionary and the resulting looked up value if present. Within our wrapper method we can use type inference tricks to avoid naming the anonymous type directly. To combine the values could construct a new type say TryGetValueResult<TValue>
struct TryGetValueResult<TValue> {
public readonly bool Success;
public readonly TValue Value;
public TryGetValueResult(bool success, TValue value) {
Success = success;
Value = value;
}
}
But I find this to be a bit heavy handed for a simple return. Instead I prefer to combine the data with the new Tuple<T1,T2> type introduced in 4.0. This type is designed to be a light weight method for combining two related values into a single instance. Perfect for this type of method.
public static Tuple<bool, TValue> TryGetValue<TKey, TValue>(
this Dictionary<TKey, TValue> map,
TKey key) {
TValue value;
var ret = map.TryGetValue(key, out value);
return Tuple.Create(ret, value);
}
Now that we’ve built our wrapper method we can go back to the original code sample and use it to access the anonymous type values
var tuple = map.TryGetValue("Joe");
if (tuple.Item1) {
Console.WriteLine(tuple.Item2);
}
This pattern is not limited strictly to TryGetValue. It’s fairly applicable anytime you need to combine a return value and one or more out parameters into a single value for reasons of type inference.
[1] Believe it or not, having a Dictionary where the value type is an anonymous type is not a wholly uncommon act. I’ve run into a bit of customer code which follows this general pattern
Comments
Anonymous
March 22, 2010
Try F#, it returns a tuple by default when functions have out parameters.Anonymous
March 23, 2010
I was wondering why you didn't just return the value, or null if not found. Then check for != null in your if() statement. I guess that wouldn't distinguish between a key that exists with a null value and a key that doesn't exist... In some situations that'd probably be appropriate, but your technique is definitely more comprehensive. Nice postAnonymous
March 23, 2010
You're right thatTryGetValueResult
is heavy handed.Tuple<>
is a great addition to the NDP, but it's too, err _light_handed. It's so generic that you have to use your brain to figure out what it's telling you. Using my brain usually gets me in to trouble. ("Is the success/failure check inItem1
orItem2
?" - if you have Dictionary<string, bool> you can't even rely on type safety to save you.) Somewhere in between isIOptional<>
, which lets you write: var result = map.TryGetValue("Joe"); if (result.HasValue) { Console.WriteLine (result.Value); } For the code, see: http://blogs.msdn.com/jaybaz_ms/archive/2004/05/06/127693.aspxAnonymous
March 24, 2010
// Nicer code + micro-optimization public static Tuple<bool, TValue> TryGetValue<TKey, TValue> ( this Dictionary<TKey, TValue> map, TKey key ) { TValue value; var ret = map.TryGetValue(key, out value); return new Tuple<bool, TValue>(ret, value); // Instead of: return Tuple.Create(ret, value); }Anonymous
March 28, 2010
Yeah, right. Better write dozen lines than one: var value = new { FirsName = "", LastName = "" }; if ( map.TryGetValue("Joe", out value) {Anonymous
March 29, 2010
Mihailik - casting by example is nice, but you're duplicating the type declaration here, and I think this might not be desirable.Anonymous
April 02, 2010
And what about: var value = map.Values.FirstOrDefault(); if (map.TryGetValue("Joe", out value)) { // ... }