【问题标题】:How to specify generic method type parameters partly如何部分指定泛型方法类型参数
【发布时间】:2011-02-04 19:09:27
【问题描述】:

我有如下扩展方法:

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

目前,我可以通过以下方式使用它:

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

它工作得很好,但是第二个类型参数真的很烦人。是否有可能在 C# 4.0 中重写 GetValueAs 是这样一种方式,该方法仍然适用于不同类型的字符串键字典,并且不需要在调用代码中指定第二个类型参数,即使用

    var list = dictionary.GetValueAs<List<int>>("A");
或至少类似于
    var list = dictionary.GetValueAs<List<int>, ?>("A");
而不是
    var list = dictionary.GetValueAs<List<int>, object>("A");

【问题讨论】:

  • 那么,基本上你要做的是从dictionary推断R的类型,而不是指定它?
  • 是的,这就是我想要的。

标签: c# generics .net-4.0 extension-methods covariance


【解决方案1】:

只要你只在对象的字典上使用它,你就可以将 T 约束为引用类型以使强制转换有效:

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

但这可能不是你想要的。请注意,C# 版本 4 doesn't solve your problem 也是。

【讨论】:

  • 你说得对,这不是我想要的。正如我所说,我想在不同类型的字符串键字典上使用该方法,即不仅在 IDictonary 上,而且在 IDictonary, IDictonary> 等上.
  • C# 不是正确的语言,静态类型检查才是王道。寻找像 Python 或 Ruby 这样的动态语言。或者通过类型擦除实现泛型的语言,例如 Java。
【解决方案2】:

怎么样

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

然后你可以做类似的事情

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

基本上要让它推断出 T 是什么,你必须在参数中包含 T 类型的东西。

编辑

也许更好的方法是

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

然后您可以使用 var 和 chain,这使您能够控制默认值。

var x = dict.GetValueAs("A", new Dictionary<string,int>).GetValueAs("B", default(int));

【讨论】:

  • 谢谢你的建议,伙计。我考虑过这样的方法。这在我看来不是很优雅。 “链调用”是不可能的,例如 (dict.GetValueAs>("A").GetValueAs("B")),使用 var 关键字是不可能的 (var x = dict.GetValueAs ("C")),无法直接从字典中返回值(return dict.GetValueAs("C");)等
  • 另一种选择是返回值,但也传递一个值只是为了获取类型,例如 var x = dict.GetValueAs("A",new Dictionary( )).GetValueAs("B", default(int));也很丑,但我能想到的唯一其他方式。
【解决方案3】:

也许您可以为这种行为创建自己的字典类:

    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 

可能也不想这样做,干草哼。

【讨论】:

  • 很好的解决方案。但是,我想使用内置 .NET IDictionary 的扩展方法,因为我引用了返回 IDictionaries 的外部代码。
【解决方案4】:

我错过了什么吗,这就是你想要的吗?也许您需要更好的转换,但对于一般演员,应该这样做:

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

简单的用法

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

使用 IDictionary,您无需指定键和值的类型,但是由于字典继承自此,因此无论您的字典是如何创建的,该函数都会保留。您甚至可以只使用对象而不是字符串作为键。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-01-22
    • 1970-01-01
    • 1970-01-01
    • 2011-11-26
    • 1970-01-01
    • 1970-01-01
    • 2018-09-12
    • 1970-01-01
    相关资源
    最近更新 更多