从'System.Collections.Generic.List <t>'到'T'没有隐式引用转换</t>

时间:2013-03-22 10:48:23

标签: c# generics

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时也可以。

有人可以告诉我为什么吗?

3 个答案:

答案 0 :(得分:20)

好的,让我们仔细看看。我们有

Update(new List<T>()); 

三位候选人 - 请注意我们只关心那些候选人的签名,因此我们将删除返回类型和约束,这些不是签名的一部分< / EM>:

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)

我们有List<T>类型的参数和类型为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) 

我们有IEnumerable<T>类型的参数,因此我们将第二个和第三个推断为:

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

现在我们有三个具有相同参数列表的适用候选者。在那里建造的那些自然比自然更差,所以我们消除第二个和第三个,只留下第一个。它赢了,没有任何限制可以被违反。

  

删除第三种方法

也可以

你的陈述是错误的;这将产生与第一个场景相同的错误。拿走第三位候选人不会导致第一位候选人突然开始击败第二位候选人。

答案 1 :(得分:12)

约束不是签名的一部分,Eric Lippert对此主题有很好的article

答案 2 :(得分:2)

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

在这种情况下的优点是可以最大限度地减少意外 - 在封面下不会发生任何意外情况;缺点是偶尔需要更详细的代码。