tags:

views:

135

answers:

5

I have a simple interface with methods such as

     bool TryGetValue(string key, out string value);
     bool TryGetValue(string key, out int value);
     bool TryGetValue(string key, out double value);
     bool TryGetValue(string key, out DateTime value);
     // only value types allowed 

     //with the implementation based on dictionary<string, object> 
     bool TryGetValue(string key, out string value)
     {
        object rc;
        if ( dict.TryGetValue(key, out rc) )
        {
            value = rc.ToString();
            return true;
        }

        value = null;
        return false;
     }

Looks like a perfect case for generics as

     bool TryGetValue<T>(string key, out T value) where T: ValueType;

except not able to work out the func implementation, anyone ?

UPDATE - the following does not compile, i'd like to avoid creating multiple TryGet... funcs !

     bool TryGetValue<T>(string key, out T value)
     {
         return dict.TryGetValue(key, out value) ;
     }
+1  A: 

Try this:

public bool TryGetValue<T>(string key, out T value) where T : struct
{
  object obj;
  var result = dict.TryGetValue(key, out obj);
  value = (T)obj;
  return result;
}

It' not pretty, but deals with the limitations of out...maybe someone can shorten it even more? If so please comment...always up to learn new ways.

Nick Craver
see update above
Kumar
@Kumar - Updated the answer...hopefully that's what you're after, if not please let me know what you're missing.
Nick Craver
A: 

Take a look at: http://msdn.microsoft.com/en-us/library/s4ys34ea.aspx it may prove useful to you. Also why not just extend Dictionary?

Woot4Moo
business rule - has to be basic value types only
Kumar
Umm, you'll have to forgive me on this one, but basic value types would be what? http://msdn.microsoft.com/en-us/library/s1ax56ch.aspx That is what msdn says about value types (structs/enums) with User-defined being one of those.
Woot4Moo
+4  A: 

I'm guessing that what you want is this:

 bool TryGetValue<TValue>(string key, out TValue value)
 {
    object rc;
    if (dict.TryGetValue(key, out rc))
    {
        value = (TValue)rc;
        return true;
    }

    value = default(TValue);
    return false;
 }

This doesn't actually convert the values if they are the wrong type - it assumes that the generic System.Object instances sitting in the dictionary are actually of type TValue when the TryGetValue method is invoked.

If you want to restrict the method to only allow value types for the generic parameter, just change the method signature to this:

 bool TryGetValue<TValue>(string key, out TValue value) where TValue : struct

Note - JaredPar had the first answer but seems to have deleted his, so I am undeleting mine because I think it's what the OP wanted. I apologize for any unintended duplication.

Aaronaught
It would probably be better to return `false` rather than throwing an InvalidCastException when there is an object associated with the key, but it's the wrong type.
Anon.
@Anon: I can see it both ways. If you return `false` then the dictionary is lying to you; it says it doesn't have the key, but it really does. If you have a typical `if (!lookup.TryGetValue(key, out value)) { lookup.Add(key, newValue) }` then it would fail unexpectedly. It depends on how this class is going to be used, though, I can see some situations in which simply returning `false` would be the desired result.
Aaronaught
that would work, to avoid the scenario you describe in comments, any way to declare dictionary<string,T> where T: ValueType without subclassing ?
Kumar
@Kumar: You can add a restriction to the `TryGetValue` method to restrict the output. You can add the same restriction to your `Add` method (assuming there is one) to restrict what goes *into* the dictionary. But you can't create an instance of `Dictionary<string, ValueType>` if that's what you're trying to do. Even with subclassing, if you create a `Dictionary<string, T> where T : ValueType` then it can only store the exact type `T` as the value, you can't throw any old `object` into it, which according to your question is what you need to be able to do.
Aaronaught
Sorry, I can't believe I wrote `where T : ValueType`... twice! The correct keyword is `struct`, I've corrected this.
Aaronaught
good answer but Nick Craver has the cleaner/shorter code... but +1 for the default keyword
Kumar
@Aaronaught - If you look in reflector `Dictionary<T,TValue>` already outputs a `default(TValue)` in your out param if the key isn't found, no need to assign it :)
Nick Craver
@Nick Craver: Yep, I know, I make good use of that behaviour and in practice I'd write this the same way you did. I wrote it a bit different for this answer just to make it as clear as possible what was going on. I'd add a second, more compact version, but then I'd just be duplicating your answer. :)
Aaronaught
A: 

I believe Aaronaught has caught the main crux of the problem (capturing the object and casting/unboxing to T), but some additional points:

  • to limit to value-types(*), you use where T : struct, not where T : ValueType...
  • ...but note that string is not a value-type, so this probably isn't a good "fit"
  • ...and (*=) note that where T : struct also excludes Nullable<T>

So I think you just need to remove that constraint completely.

Marc Gravell
A: 

I'd probably go for something like this, although as Marc points out the struct constraint won't allow strings:

private static class Container<T> where T : struct
{
    public static Dictionary<string, T> Dict = new Dictionary<string, T>();
}

public bool TryGetValue<T>(string key, out T value) where T : struct
{
    return Container<T>.Dict.TryGetValue(key, out value);
}
kvb