【问题标题】:There is no implicit reference conversion from 'System.Collections.Generic.List<T>' to 'T'没有从 'System.Collections.Generic.List<T>' 到 'T' 的隐式引用转换
【发布时间】:2013-03-22 10:48:22
【问题描述】:
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&lt;T&gt;() { entity }) 替换为Update((new List&lt;T&gt;() { entity }).AsEnumerable()),就可以了。

删除第三种方法Update&lt;TSub&gt;(TSub entity) where TSub : T也可以。

谁能告诉我为什么?

【问题讨论】:

标签: c# generics


【解决方案1】:

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

Update(new List<T>()); 

还有三个候选者——请注意,我们只关心这些候选者的签名,所以我们将去掉返回类型和约束,它们不是签名的一部分:

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

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

考虑第二种方法

Update<U>(U entity) 

我们有一个List&lt;T&gt; 类型的参数和一个形参U。因此我们推断UList&lt;T&gt;

考虑第三种方法:

Update<V>(IEnumerable<V> entities)

我们有一个List&lt;T&gt; 类型的参数和一个IEnumerable&lt;V&gt; 类型的形参。 List&lt;T&gt; 实现 IEnumerable&lt;T&gt; 所以我们推断 V 是 T。

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

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

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

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

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

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

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

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

如果我打电话给Update((new List&lt;T&gt;() { entity }).AsEnumerable()),就可以了。

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

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

我们有一个IEnumerable&lt;T&gt; 类型的参数,所以我们推断第二个和第三个是:

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

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

删除第三种方法也可以

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

【讨论】:

  • 如果我们让约束成为方法签名的一部分,我仍然不太明白为什么编译器需要“猜测”。编译器检查第三种方法(Update(...)),然后推断泛型为 List,然后发现 List 不是 T,然后消除第三个。
  • @MouhongLin:请参阅安德鲁回答中引用的文章。我已经尝试解释了十几次,为什么作为签名的一部分的约束会使重载解析的结果变得更糟,几乎没有人同意我的观点。
  • @eric-lippert,感谢您的热情回复。现在对我来说很清楚了。但我还有一个问题。 '删除第三种方法也可以'。当我完全删除第三种方法时,我没有收到错误。你能告诉我原因吗?
  • @JailuLee:发布一个演示行为的小程序,以便我们知道我们在谈论同一件事。
【解决方案2】:

约束不是签名的一部分,Eric Lippert 有一个很棒的article 关于这个主题。

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 其实是List&lt;T&gt;IEnumerable&lt;TSub&gt;的演员表
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-17
    • 1970-01-01
    • 1970-01-01
    • 2011-01-01
    • 1970-01-01
    • 2014-04-19
    • 1970-01-01
    相关资源
    最近更新 更多