为什么这样做:
public IList<ICoupon> GetCouponsForSite(string siteSlug)
{
var coupons = _db.Coupons.Where(x => x.Site.slug == siteSlug)
.Select(x => new Coupon(x.id));
var list = new List<ICoupon>();
foreach (var coupon in coupons)
{
list.Add(coupon);
}
return list;
}
但这确实不起作用(错误 - 无法将表达式转换为返回类型):
public IList<ICoupon> GetCouponsForSite(string siteSlug)
{
return _db.Coupons.Where(x => x.Site.slug == siteSlug)
.Select(x => new Coupon(x.id)).ToList();
}
答案 0 :(得分:10)
因为db.Coupons ... ToList()返回IList<Coupon>
而不是IList<ICoupon>
。 IList<Coupon>
并非源自IList<ICoupon>
,因为C#3不支持通用差异。 (C#4确实支持通用方差,但在这种情况下它仍然不会得到。考虑到收到IList<ICoupon>
的人可能会尝试将SomeEvilTypeThatImplementsICoupon填充到其中。但是IList<Coupon>
无法接受因为SomeEvilTypeThatImplementsICoupon不是从优惠券派生的。请参阅http://hestia.typepad.com/flatlander/2008/12/c-covariance-and-contravariance-by-example.html以讨论这个可兑换问题,尽管情况略有不同,并且Eric Lippert文章从那里链接。)
(相比之下,您的第一个代码段显式构造了一个List<ICoupon>
,其中可以包含任何实现ICoupon的内容,然后将一些Coupon对象放入该列表中。现在,如果接收者决定将SomeEvilTypeThatImplementsICoupon戳入其中,一切都很好,因为List是为了保存任何ICoupon,而不仅仅是实际的Coupon对象。)
答案 1 :(得分:4)
它无法隐式地投射List&lt;优惠券&gt;列出&lt; ICoupon&gt;。试试这个:
public IList<ICoupon> GetCouponsForSite(string siteSlug)
{
return _db.Coupons.Where(x => x.Site.slug == siteSlug)
.Select(x => new Coupon(x.id)).Cast<ICoupon>().ToList();
}
这个的基本原因是,如果您有一个class FancyCoupon : ICoupon
,并试图将其放入List<Coupon>
,那么它会失败,因为FancyCoupon不是来自优惠券(只有ICoupon)但是它应该适用于List<ICoupon>
。因此,乍一看它看起来应该能够使用一个作为另一个,这两种类型之间存在相当重要的差异。
演员调用基本上遍历列表并对每个列表进行类型转换以获得新列表(由于性能原因,在引擎盖下有更多内容,但出于实际目的,您可以这样想。)
(已从评论中修复)
答案 2 :(得分:0)
IQueryable<ICoupon>
不是来自IList<ICoupon>
。
答案 3 :(得分:0)
这是因为编译器在ICoupon
中推断Coupon
而不是Select
作为泛型类型参数。因此,不是在其他人提出的Select
之后进行显式强制转换(因为它需要迭代所有项目而不是太有效),您也可以通过指定正确的{{{}来使用隐式强制转换(或者更正确的方差)。 3}}:
public IList<ICoupon> GetCouponsForSite(string siteSlug)
{
return _db.Coupons.Where(x => x.Site.slug == siteSlug)
.Select<?, ICoupon>(x => new Coupon(x.id)).ToList();
}
(您需要将?
替换为Coupons
集合的相应类型。)