c#中的通用接口类型推断怪异

Imp*_*rks 7 .net c# generics type-inference json.net

在这个非常明显的情况下,C#无法推断出类型参数:

public void Test<T>(IEnumerable<KeyValuePair<string, T>> kvp)
{
    Console.WriteLine(kvp.GetType().Name + ": KeyValues");
}

Test(new Newtonsoft.Json.Linq.JObject());
Run Code Online (Sandbox Code Playgroud)

JObject类型明确实现IEnumerable<KeyValuePair<string, JToken>>,但我收到以下错误:

CS0411: The type arguments for method cannot be inferred from the usage.
Run Code Online (Sandbox Code Playgroud)

为什么会这样?

UPD:对于将此问题标记为重复的编辑:请注意我的方法签名不接受IEnumerable<T>,但是IEnumerable<KeyValuePair<string, T>>.该JObject类型实现IEnumerable两次,但只有一个实现匹配此约束 - 因此不应该有歧义.

UPD:这是一个完整的自包含repro没有JObject:https: //gist.github.com/impworks/2eee2cd0364815ab8245b81963934642

Eri*_*ert 13

这是一个更简单的复制品:

interface I<T> {}
class X<T> {}
class Y {}
class Z {}  
class C : I<X<Y>>, I<Z> {}
public class P
{   
    static void M<T>(I<X<T>> i) { }
    public static void Main()
    {
        M(new C());
    }
}
Run Code Online (Sandbox Code Playgroud)

类型推断失败.你问为什么,以及为什么问题总是难以回答,所以让我重新解释一下这个问题:

规范的哪一行不允许这种推断?

我手头有C#3规范的副本; 这条线如下

  • V就是我们推断类型,所以I<X<T>>在这种情况下,
  • U就是我们推断类型,所以C在这种情况下,

这在C#4中略有不同,因为我添加了协方差,但出于本讨论的目的,我们可以忽略它.

...如果V是构造类型C<V1, … Vk>和有一组独特的类型U1, … Uk ,使得隐式转换从存在UC<U1, … Uk>随后的精确推断从每个由Ui到对应的Vi.否则不做推论.

注意那里的单词是唯一的.没有一套独特的类型,从而C可以转换为I<Something>,因为这两个X<Y>Z有效.

这是另一个非原因问题:

设计团队做出此决定时考虑了哪些因素?

你是对的,从理论上讲,我们可以在你的情况下发现X<Y>有意和Z无意.如果你想看提出了类型推断算法,可以处理这样永不犯错非特殊情况-记住,Z可能是一个或亚型的超X<Y>或者X<Something Else>I可能是协变的-那么我相信,在C#团队很乐意考虑您的提案.

我们在2005年设计了C#3类型推理算法,并确定一个类实现两个相同接口的场景很少,并且处理这些罕见的情况会导致该语言出现相当大的复杂情况.这些复杂因素在设计,指定,实施和测试方面都很昂贵,而且我们还有其他需要花钱和努力才能产生更大影响的事情.

此外,我们不知道我们何时制作C#3是否会在C#4中添加协方差.我们从不想引入新的语言功能,使未来可能的语言功能变得不可能或困难.现在最好在语言中加入限制,并考虑在以后删除它们,而不是为了在下一版本中使常见场景变得困难的罕见场景做大量工作.

事实上,我帮助设计了这个算法并多次实现它,并且最初完全不记得这个规则应该告诉你在过去的13年里这种情况经常出现的频率.几乎没有.有人在您的特定船上很少见,并且有一个简单的解决方法:指定类型.

你想到的一个问题但想到的是:

错误信息会更好吗?

是的.我为此道歉.我做了很多工作,使得重载解析错误消息对于常见的LINQ场景更具描述性,并且我一直希望返回并使其他类型的推理错误消息更加清晰.我编写了类型推断和重载解析代码,以维护内部信息,解释为什么推断了类型或者选择或拒绝了重载,这既用于我自己的调试目的,也用于制作更好的错误消息,但我从来没有暴露过给用户的信息.总有一些优先事项.

您可以考虑在Roslyn GitHub网站上输入一个问题,建议改进此错误消息以帮助用户更轻松地诊断情况.同样,我没有立即诊断问题并且不得不回到规范的事实表明该消息不清楚.