views:

149

answers:

3

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");

+1  A: 

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.

Hans Passant
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<string, object>, but on IDictonary<string, int>, IDictonary<string, IEnumerable<object>>, etc.
DNNX
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
A: 

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
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<IDictionary<string, int>>("A").GetValueAs<int>("B")), using var keyword is impossible (var x = dict.GetValueAs<int>("C")), impossible to return value from dictionary directly (return dict.GetValueAs<int>("C");), etc.
DNNX
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<string,int>()).GetValueAs("B", default(int)); Also ugly but the only other way I can think of.
juharr
A: 

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
Good solution. However, I want to use extension methods for built-in .NET IDictionary because I refer to external code which returns IDictionaries.
DNNX