C# 泛型:为什么可以';在这种情况下,编译器不能推断类型参数吗?

C# 泛型:为什么可以';在这种情况下,编译器不能推断类型参数吗?,c#,generics,type-inference,C#,Generics,Type Inference,我想写一个扩展方法,它可以在值是某种序列的字典上工作。不幸的是,编译器似乎无法从我对该方法的使用中推断出泛型参数;我需要明确地指定它们 public static void SomeMethod<TKey, TUnderlyingValue, TValue> (this IDictionary<TKey, TValue> dict) where TValue : IEnumerable<TUnderlyingValue> { } st

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

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>>();
}
publicstaticvoid方法
(本词典)
其中TValue:IEnumerable{}
静态void用法()
{
var dict=新字典();
var dict2=新字典();
//这些不可编译
dict.somethod();
SomeMethod(dict);//与扩展方法无关
dict2.SomeMethod();//希望这更容易推断,但没有乐趣
//这些很好用
dict.somethod();
dict2.SomeMethod();
}
我意识到类型推断不是一门精确的科学,但我想知道这里是否缺少一些基本的“规则”——我不熟悉规范的细节

  • 这是推理过程的一个缺点,还是我期望编译器在这种情况下“找出它”是不合理的(可能是歧义)
  • 我是否可以更改方法的签名,使其具有相同的功能,但“可推断”

  • 为什么不省略IEnumerable的类型呢

    public static void SomeMethod<TKey, TValue>
    (this IDictionary<TKey, TValue> dict)
    where TValue : IEnumerable { }    
    
    publicstaticvoid方法
    (本词典)
    其中TValue:IEnumerable{}
    
    C#类型推断不能脱离约束或返回值。所以你的运气会好一点

    public static void SomeMethod<TKey, TUnderlyingValue>
        (this IDictionary<TKey, IEnumerable<TUnderlyingValue>> dict)
      { }
    
    publicstaticvoid方法
    (本词典)
    { }
    
    如果您将参数声明为
    newdictionary()
    ,那么这将起作用,但如果您将其声明为
    newdictionary()
    ,则不起作用

    我不得不说,按照我阅读的第7.5.2节的方式,似乎由于
    列表
    实现了
    IEnumerable
    ,因此
    TUnderlyingValue
    的类型推断应该可以工作。然而,这一部分并不容易理解。我假设它不能在多个“层”中工作,因为
    SomeMethod(IEnumberable val){}
    使用
    SomeMethod(new List())
    调用它就可以了。我在规范中没有具体看到任何关于解析
    U=Ca
    类型的内容,因此可能没有定义该级别的推理

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

    我不确定我是否同意。规格相当详细

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

    您缺少的基本规则可能是约束不是签名的一部分。类型推断是从签名开始的

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

    这是推理过程的一个缺点吗

    可以说,是的。在我看来,考虑到相互竞争的设计要求,这是一个合理的选择。(那些是“按用户的意思做”和“当事情看起来模棱两可时给出错误”。)

    在这种情况下,我对编译器应该“弄明白”的期望是否不合理

    不,你看起来是个通情达理的人,你的期望似乎是建立在良好的推理基础上的。然而,完全有可能有一个合理的期望,尽管它没有得到满足。这就是其中之一

    我是否可以更改方法的签名,使其具有相同的功能,但“可推断”

    这将很困难,因为泛型字典类型在其转换中不是协变或逆变的。要捕获的概念在类型系统中不容易以提供推断的方式表达


    如果你更喜欢使用更高级类型推理的语言,请考虑使用f*。如果你偏爱倾向于“做用户的意思”的语言,而不是“在歧义上报告错误”,那么考虑使用VB.< /P>推断TunLyLink值可能是困难的。特别是由于

    IEnumerable
    的类型参数在.NET4中是协变的。类型推理是一门精确的科学,有正式的算法,它不仅仅是一种猜测。如果编译器无法推断类型,那只是因为它没有处理某些情况的特定规则,而且这通常是一种实现设计,而不是限制。看看这里:@Jack:谢谢你的链接;那很有趣。请看我对埃里克·利珀特回答的评论,了解我的意思。我想我用词不好。+1@Philip Rieck:谢谢。我知道这种解决方法,但它不能解决我感兴趣的一般情况。@Ani see edits-我不确定在c#4.0中定义和实现的类型推理是否支持这种推理级别。这对Dictionary不起作用,因为IDictionary是一种不变类型。假设方法体包含以下内容:
    dict.Add(newobservetecollection())。如果存在某种类型的IReadOnlyDictionary接口,则可以使其在value参数上协变,并允许两个字典都工作。因为这样,您将无法(缺少反射)访问基础值的类型-您所知道的只是每个
    都是一个序列,但这并不是什么东西。你可以将TValue转换为IEnumerable,因为你知道它是这样的类型。非常棒,谢谢——“约束不是签名的一部分”是我所缺少的,我必须对此绞尽脑汁。另一方面,我所说的“非精确科学”是指两种不同但有效的推理算法(一般来说,不是特定于规范)可以推断出类型