【发布时间】:2018-05-16 13:32:13
【问题描述】:
所以我遇到了一个问题,编译器告诉我我不能做一些我认为我应该能够做的事情。我已经简化了,代码如下
class Program
{
static IEnumerable<BaseInterface> GetSubInterfaceImplementers<T>()
where T : BaseInterface
{
return GetThings().OfType<T>();
}
private static IEnumerable<BaseInterface> GetThings()
{
return Enumerable.Empty<BaseInterface>();
}
}
public interface BaseInterface { }
public interface SubInterface1 : BaseInterface { }
public interface SubInterface2 : BaseInterface{ }
所以这行return GetThings().OfType<T>(); 给了我这个错误:
无法将类型“System.Collections.Generic.IEnumerable
”隐式转换为“System.Collections.Generic.IEnumerable ”。存在显式转换(您是否缺少演员表?)
简而言之,它不能将IEnumerable<T> 转换为IEnumerable<BaseInterface>,即使我明确表示 T 必须是 BaseInterface 类型。
如果在该方法中我将代码更改为这样读取
return GetThings().OfType<SubInterface1>();
由于 IEnumerable 是 'Co' 或 'Contra' 变体(我不记得了),它可以编译,因此返回子类型显然没有问题。
那为什么不编译呢??
显式转换工作,如下所示:
return (IEnumerable<BaseInterface>)GetThings().OfType<T>();
但是为什么编译器不能推断出来
【问题讨论】:
-
添加
where T : class, ...似乎有帮助,所以这可能是因为这种转换的确切工作取决于编译器是否知道它将是一个类。 -
您不使用
static IEnumerable<T> GetSubInterfaceImplementers<T>() where T : BaseInterface { return GetThings().OfType<T>(); }的原因是什么? -
虽然该错误信息是正确的,但它可能会更清楚。对于那个很抱歉!您可能会考虑在 roslyn github 站点上输入问题,也许有人可以对其进行改进,以便错误指出转换不起作用的原因是因为协方差的限制。
-
编译器开发人员最容易做的事情就是简单地说明违反了语言的哪个规则:你正在从 X 隐式转换到 Y 并且没有这样的转换。更难的是从错误的代码中推断出开发人员的心理状态和意图,并给出针对该场景定制的错误消息。我为 LINQ 做了很多后者的工作。当你说
from c in customers select c.FristName时,简单的事情就是说Select没有与参数(customers, c=>c.FristName)一起使用的重载! -
但这当然不是开发人员想要或需要听到的。同样,我们不想说重载解析失败是因为类型推断失败。我们想说的是
FristName是FirstName的错字。 C# 在这方面做得很好,但工作量很大。我们没有在与协方差相关的错误消息中投入任何甚至接近相似数量的工作,它显示了。