简体   繁体   中英

How to specify generic method type parameters partly

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

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.

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

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM