为什么带有约束的通用扩展方法不被识别为扩展方法?

5

可能重复:
泛型扩展方法没有类型推断

考虑以下两种方法:

public static IEnumerable<V> Merge<V>
       (this IEnumerable<IEnumerable<V>> coll)

public static IEnumerable<V> Merge<T, V>
       (this IEnumerable<T> coll) 
              where T : IEnumerable<V>

两种写法都可以很好地编译通过,对于泛型类型,编译期间调用者已知其类型,因此也就知道了扩展类型的确切类型。

你可以成功地调用这两个方法,但只有第一个方法作为扩展方法才有效。

为什么呢?

更新1

为了演示失败情况,使用第二种方法并以以下示例:

    var x = new List<List<int>>();
    var y = x.Merge();

更新 -- 结束

你们不觉得原帖子过于复杂,难以理解吗?出于教育目的,即使在技术上(也就是答案方面)属于重复,我认为这篇文章不应该被关闭。这只是我的个人看法。


啊,我懂了,你说使用第二种方法时,是指第二个方法单独存在。并不是这两个方法互相重载... 对吗? - James Michael Hare
1
在这种情况下,Eric 提供给你的 SO 答案就是实际原因。因为 T 和 V 之间的关系在约束中已经指定,而编译器不会从约束中推断类型,所以编译器不知道如何解析你的泛型类型参数 V,这就是为什么你必须显式地指定类型参数 V 的原因。 - James Michael Hare
@James Michael Hare,我不知道模板参数必须被解析,而不仅仅是方法;-)现在理解编译器变得非常容易(但如果约束可以成为方法定义的一部分,那就更好了)。 - greenoldman
2个回答

4

方法类型推断在做推断时不会考虑约束条件。

这个问题昨天也被问到过。请查看我在那里的回答以获取更多详细信息。

没有泛型扩展方法的类型推断


我可能漏掉了什么,或者你的简短回答在这里不够充分,“从参数和形式参数中进行推断”。应该可以(第二种情况)获取参数的类型,它是T,就像James的例子中一样,可能是List<int>。所以T是已知的,扩展应该可以工作。或者不应该?;-) 我需要时间让它沉淀... - greenoldman
1
@macias:你所拥有的事实是(1)T 是 List<int>,并且(2)T 受限于 IEnumerable<V>。我们不从约束条件中进行推断。因此事实(2)从未被考虑,因此我们没有任何可以推断 V 的依据。 - Eric Lippert
啊,现在我明白了。如果你还在阅读我的评论,请澄清你的博客文章,你指的是模板参数,而不是方法参数。我不知道为什么,但当我阅读你的答案和博客并看到“参数”时,我总是将其与方法参数联系起来,而不是模板参数(我应该这样做)。现在完全清楚了!谢谢。 - greenoldman
@macias:不用谢!我同意,这些术语很令人困惑。我们有“参数”和“形式参数”,然后类似地有“类型参数”和“类型参数”。很容易在使用一个术语时意思实际上是另一个,这可能会非常令人困惑。 - Eric Lippert

2

我认为问题不在于第二个无法被调用,而是 IntelliSense 无法看到它,因为它不能轻易地推断出你的调用中的第二个泛型类型参数 V,除非有明确的帮助。

例如,给定您的两个扩展方法,以下都是合法的:

    // IEnumerable<IEnumerable<int>> definition...
    List<List<int>> x = ...;

    // calls your first method (implicitly)
    x.Merge();

    // also calls your first method (explicitly)
    x.Merge<int>();

    // calls your second method (explicitly)
    x.Merge<List<int>, int>();

这三个都编译成功了,我只是认为对于两个泛型类型参数,它无法从使用中“推断”出第二个泛型类型参数,因此它不会在智能提示中显示,但仍然合法...
更新:根据提问者的说法,这两种方法不是声明为重载,而是要么...要么...。由于T和V之间的关系在类型约束中定义,因此在类型推断中不会被使用,因此Merge()不适用于第二种形式。

我是说编译器,而不是IntelliSense。 对我来说,编写第一个方法并使用它,然后将此方法替换为第二个方法就足够了。 在第一种情况下,代码可以正常编译,在第二种情况下则不行。 顺便说一句,您忘记了第四种情况-x.Merge()的第二个方法。 我希望它也可以编译。 - greenoldman
@macias:您能给一个无法编译的调用例子吗?当您说它无法编译时,是指使用推断吗?还是没有使用推断? - James Michael Hare
我更新了问题并附上了简短的示例。 - greenoldman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接