5

I have an extension method like below:

public static T GetValueAs<T, R>(this IDictionary<string, R> dictionary, string fieldName)
    where T : R
{
    R value;
    if (!dictionary.TryGetValue(fieldName, out value))
        return default(T);

    return (T)value;
}

Currently, I can use it in the following way:

    var dictionary = new Dictionary<string, object>();
    //...
    var list = dictionary.GetValueAs<List<int>, object>("A"); // this may throw ClassCastException - this is expected behavior;

It works pretty fine, but the second type parameter is really annoying. Is it possible in C# 4.0 rewrite GetValueAs is such a way that the method will still be applicable to different types of string-keyed dictionaries AND there will be no need to specify second type parameter in the calling code, i.e. use

    var list = dictionary.GetValueAs<List<int>>("A");
or at least something like
    var list = dictionary.GetValueAs<List<int>, ?>("A");
instead of
    var list = dictionary.GetValueAs<List<int>, object>("A");
leppie
  • 115,091
  • 17
  • 196
  • 297
DNNX
  • 6,215
  • 2
  • 27
  • 33

4 Answers4

1

As long as you only use it on dictionaries of object, you can constrain T to be a reference type to make the cast valid:

public static T GetValueAs<T>(this IDictionary<string, object> dictionary, string fieldName)
  where T : class {
  object value;
  if (!dictionary.TryGetValue(fieldName, out value))
    return default(T);

  return (T)value;
}

But that's probably not what you want. Note that C# version 4 doesn't solve your problem either.

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • You're right, that's not what I want. As I said, I want to use the method on different types of string-keyed dictionaries, i.e. not only on IDictonary, but on IDictonary, IDictonary>, etc. – DNNX Apr 06 '10 at 13:42
  • C# is just not the right language for this, static type checking is King. Look for a dynamic language like Python or Ruby. Or a language that implements generics through type erasure, like Java. – Hans Passant Apr 06 '10 at 13:48
0

What about

public static void GetValueAs<T, R>(this IDictionary<string, R> dictionary, string fieldName, out T value)
    where T : R
{
    value = default(T);
    dictionary.TryGetValue(fieldName, out value)
}

Then you can do something like

List<int> list;
dictionary.GetValueAs("fieldName", out list);

Basically to have it infere what T is you have to have something of type T in the parameters.

Edit

Maybe a better way would be

public static T GetValueAs<T, R>(
    this IDictionary<string, R> dictionary, 
    string fieldName, 
    T defaultValue)
    where T : R
{
    R value = default(R);
    return dictionary.TryGetValue(fieldName, out value) ? 
        (T)value : defaultValue;
}

Then you can use var and chain and this gives you the ability to control what the default is.

var x = dict.GetValueAs("A", new Dictionary<string,int>).GetValueAs("B", default(int));
juharr
  • 31,741
  • 4
  • 58
  • 93
  • Thank you for advice, man. I've considered such an approach. This is not very elegant to my mind. "Chain calls" are impossible, for example (dict.GetValueAs>("A").GetValueAs("B")), using var keyword is impossible (var x = dict.GetValueAs("C")), impossible to return value from dictionary directly (return dict.GetValueAs("C");), etc. – DNNX Apr 06 '10 at 15:55
  • Another options is to return the value, but to also pass in a value just to get the type so something like var x = dict.GetValueAs("A",new Dictionary()).GetValueAs("B", default(int)); Also ugly but the only other way I can think of. – juharr Apr 08 '10 at 23:19
0

Maybe you could make your own dictionary class for this behaviour:

    public class CastableDictionary<TKey, TValue> : Dictionary<TKey, TValue>
        {
            public TOut GetValueAs<TOut>(TKey key) where TOut : TValue
            {
                TValue result;
                if (this.TryGetValue(key, out result))
                {
                    return (TOut)result;
                }
                return default(TOut);
            }
        }



var d = new CastableDictionary<string, object>();

        d.Add("A", 1);

        d.Add("B", new List<int>() { 1, 2, 3});

        var a = d.GetValueAs<int>("A"); // = 1

        var b = d.GetValueAs<List<int>>("B"); //= 1, 2, 3 

Probably don't want to do this either hay hum.

Hath
  • 12,606
  • 7
  • 36
  • 38
  • Good solution. However, I want to use extension methods for built-in .NET IDictionary because I refer to external code which returns IDictionaries. – DNNX Apr 06 '10 at 15:59
0

Am I missing something, surely this is all you want? Maybe you need better conversion, but for a general cast, this should do:

public static T getValueAs<T>(this IDictionary dict, string key)
{            
    try
    {
        return (T)dict[key];
    } catch
    {
        return default(T);
    }            
}

usage simply being

MyDictionary.getValueAs<Int32>("hello");

With IDictionary you don't need to specify the types for the key and value, however as dictionary inherits from this the function remains, regardless of how your dictionary is created. You can even just use object instead of string for the key.