21
class Class1<T>
{
    public virtual void Update(T entity)
    {
        Update(new List<T>() { entity }); //It's failed
    }

    public virtual void Update(IEnumerable<T> entities)
    {
    }

    public virtual void Update<TSub>(TSub entity) where TSub : T
    {
    }

    public virtual void Update<TSub>(IEnumerable<TSub> entities) where TSub : T
    {
    }
}

我有一段代码。但它总是失败。

如果我替换Update(new List<T>() { entity })Update((new List<T>() { entity }).AsEnumerable()),就可以了。

删除第三种方法也可以Update<TSub>(TSub entity) where TSub : T

谁能告诉我为什么?

4

3 回答 3

20

好的,让我们仔细检查一下。我们有

Update(new List<T>()); 

还有三个候选人——请注意,我们只关心那些候选人的签名,所以我们将去掉不属于签名的返回类型和约束:

Update(IEnumerable<T> entities)
Update<U>(U entity) 
Update<V>(IEnumerable<V> entities) 

我们的第一个任务是对最后两个候选者进行类型推断。如果推理失败,则它们不是适用的候选者。

考虑第二种方法

Update<U>(U entity) 

我们有一个类型List<T>参数和一个形参U。因此我们推断UList<T>

考虑第三种方法:

Update<V>(IEnumerable<V> entities)

我们有一个 type 的参数List<T>和一个 type 的形参IEnumerable<V>List<T>实现IEnumerable<T>所以我们推断V是T。

好的,所以我们的候选列表现在包括:

Update(IEnumerable<T> entities)
Update<List<T>>(List<T> entity) 
Update<T>(IEnumerable<T> entities) 

所有这些候选人都适用吗?是的。在每种情况下List<T>都可以转换为形式参数类型。我们还不能消除它们中的任何一个。

既然我们只有适用的候选人,我们必须确定哪一个是唯一最好的。

我们可以立即消除第三个。第三个和第一个在形式参数列表中是相同的。C# 的规则是,当您有两个在其形式参数列表中相同的方法时,其中一个“自然地”到达那里并且其中一个通过类型替换到达那里,被替换的那个就失败了。

我们也可以消除第一个。显然,第二个中的完全匹配优于第一个中的不完全匹配。

这使得第二个成为最后一个站着的人。它赢得了过载解决之战。然后在最终验证期间,我们发现违反了约束:List<T>不保证是 的派生类T

因此重载决议失败。您的论点导致选择的最佳方法无效。

如果我打电话Update((new List<T>() { entity }).AsEnumerable()),那就没问题了。

正确的。再过一遍。三名候选人:

Update(IEnumerable<T> entities)
Update<U>(U entity) 
Update<V>(IEnumerable<V> entities) 

我们有一个 type 的参数IEnumerable<T>,所以我们推断第二个和第三个是:

Update(IEnumerable<T> entities)
Update<IEnumerable<T>>(IEnumerable<T> entity) 
Update<T>(IEnumerable<T> entities) 

现在我们有了三个具有相同参数列表的适用候选。那些正在建设中的自然会比自然的更糟糕,所以我们消除了第二个和第三个,只留下第一个。它赢了,它没有被违反的限制。

删除第三种方法也可以

您的陈述是错误的;这将产生与第一种情况相同的错误。带走第三位候选人不会导致第一位候选人突然开始击败第二位候选人。

于 2013-03-22T16:35:45.233 回答
12

约束不是签名的一部分,Eric Lippert 有一篇关于这个主题的好文章。

于 2013-03-22T11:14:01.993 回答
2

List<T>您本质上是在问为什么编译器没有从to创建隐式转换IEnumerable<T>。原因是 C# 团队做出了深思熟虑的设计决定,即必须由程序员而不是编译器解决潜在的歧义情况。(请注意,VB.NET 团队做出了不同的决定,始终尝试与感知到的程序员意图一致的合理事情。)

在这种情况下的好处是可以最大限度地减少意外——在幕后不会发生任何意外;缺点是偶尔需要更冗长的代码。

于 2013-03-22T11:10:16.693 回答