Поделиться через


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 post

  • Anonymous
    March 23, 2010
    You're right that TryGetValueResult is heavy handed. Tuple&lt;&gt; 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 in Item1 or Item2?" - if you have Dictionary<string, bool> you can't even rely on type safety to save you.) Somewhere in between is IOptional&lt;&gt;, 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.aspx

  • Anonymous
    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))  {    // ...  }