泛型:为什么编译器在这种情况下不能推断出类型参数?

时间:2010-10-19 13:27:34

标签: c# generics type-inference

我想编写一个扩展方法,该方法适用于值为某种序列的字典。不幸的是,编译器似乎无法从我对该方法的使用中推断出泛型参数;我需要明确指定它们。

public static void SomeMethod<TKey, TUnderlyingValue, TValue>
    (this IDictionary<TKey, TValue> dict)
    where TValue : IEnumerable<TUnderlyingValue> { }    

static void Usage()
{
    var dict = new Dictionary<int, string[]>();
    var dict2 = new Dictionary<int, IEnumerable<string>>();

    //These don't compile
    dict.SomeMethod();
    SomeMethod(dict); // doesn't have anything to do with extension-methods
    dict2.SomeMethod(); // hoped this would be easier to infer but no joy


    //These work fine
    dict.SomeMethod<int, string, string[]>();
    dict2.SomeMethod<int, string, IEnumerable<string>>();
}

我意识到类型推断不是一门精确的科学,但我想知道是否有一些基本的'规则'我在这里缺失 - 我不熟悉规范的细节。

  1. 这是推理过程的缺点还是我期望编译器在这种情况下应该“弄明白”不合理(或许是歧义)?
  2. 我可以改变方法的签名,使其具有同等功能但“可推断”吗?

3 个答案:

答案 0 :(得分:15)

  

我意识到类型推断不是一门精确的科学

我不确定我是否同意。规范非常详细。

  

我想知道这里是否有一些基本的“规则”

您缺少的基本规则可能是约束不是签名的一部分。类型推断在签名之外起作用。

我认为这个设计决定的理由很充分。然而,许多人认为我认为有充分的理由做出设计决定,这在道德上是错误的。如果你有兴趣阅读关于我是对还是错的主题几百万字,请看我关于这个主题的文章和一百多条评论告诉我我错了:

http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

  

这是推理过程的缺点吗?

可以说是的。在我看来,根据竞争设计要求,这是一个合理的选择。 (那些“做用户意味着什么”和“当事情看起来模糊不清时给出错误”。)

  

我期望编译器在这种情况下应该“弄明白”不合理吗?

没有。你似乎是一个理智的人,你的期望似乎是基于良好的推理。然而,完全有可能有一个合理的期望,但仍未得到满足。这将是其中一种情况。

  

我可以改变方法的签名,使其具有同等功能但“可推断”吗?

这很难,因为通用的Dictionary类型在其转换中不是协变的或逆变的。您想要捕获的概念不容易在类型系统中以提供推理的方式表达。

如果您更喜欢使用具有更高级类型推断的语言,请考虑使用F#。如果你喜欢倾向于“做用户意味着”的语言,而不是“报告歧义上的错误”,请考虑使用VB。

答案 1 :(得分:4)

C#类型推断不受约束或返回值的影响。所以你稍微运气好

public static void SomeMethod<TKey, TUnderlyingValue>
    (this IDictionary<TKey, IEnumerable<TUnderlyingValue>> dict)
  { }

如果您将参数声明为new Dictionary< string, IEnumerable<int>>(),那么这将有效,但如果您声明new Dictionary<string, List<int>>(),则不是

我必须说,我阅读c# spec第7.5.2节的方式,似乎自List<int>实现IEnumerable<int>后,TUnderlyingValue的类型推断} 应该管用。但是,该部分并不是很容易理解的。我认为它不能通过多个“层”工作,因为SomeMethod<T>(IEnumberable<T> val){}可以正常使用SomeMethod(new List<string>())调用它。我没有在规范中看到任何涉及解析U = Ca<Va, Cb<Vb>>类型的内容,因此可能没有定义该级别的推断。

答案 2 :(得分:1)

为什么不省略IEnumerable的类型?

public static void SomeMethod<TKey, TValue>
(this IDictionary<TKey, TValue> dict)
where TValue : IEnumerable { }