接口列表与派生类型列表 – 无法将表达式类型转换为返回类型

为什么这样做:

public IList GetCouponsForSite(string siteSlug) { var coupons = _db.Coupons.Where(x => x.Site.slug == siteSlug) .Select(x => new Coupon(x.id)); var list = new List(); foreach (var coupon in coupons) { list.Add(coupon); } return list; } 

但这确实不起作用(错误 – 无法将表达式转换为返回类型):

 public IList GetCouponsForSite(string siteSlug) { return _db.Coupons.Where(x => x.Site.slug == siteSlug) .Select(x => new Coupon(x.id)).ToList(); } 

因为db.Coupons … ToList()返回IList而不是IListIList不是来自IList因为C#3不支持通用差异。 (C#4确实支持通用方差,但在这种情况下仍然不会得到。考虑到收到IList可能会尝试将SomeEvilTypeThatImplementsICoupon填充到其中。但IList无法接受,因为SomeEvilTypeThatImplementsICoupon不是来自优惠券。请参阅http://hestia.typepad.com/flatlander/2008/12/c-covariance-and-contravariance-by-example.html以讨论此可兑换问题,尽管情况略有不同,以及从那里链接的Eric Lippert文章。)

(相比之下,你的第一个片段显式构造了一个List ,它可以包含任何实现ICoupon的东西,然后将一些Coupon对象放入该列表。现在如果接收者决定将SomeEvilTypeThatImplementsICoupon戳入其中,一切都很好,因为列表是为了保存任何ICoupon,而不仅仅是实际的优惠券对象。)

它不能隐式地将List 转换为List 。 试试这个:

 public IList GetCouponsForSite(string siteSlug) { return _db.Coupons.Where(x => x.Site.slug == siteSlug) .Select(x => new Coupon(x.id)).Cast().ToList(); } 

这个的基本原因是,如果你有一个class FancyCoupon : ICoupon并试图把它放入List然后它会失败因为FancyCoupon不是来自优惠券(只有ICoupon)但它应该工作得很好到List 。 因此,初看起来它看起来应该能够使用一个作为另一个,这两种类型之间存在相当重要的差异。

演员调用基本上遍历列表并对每个新列表进行类型转换(由于性能原因,在引擎盖下有更多的内容,但出于实际目的,您可以这样思考)。

(更新了评论中的修复)

IQueryable不是从IList派生的。

这是因为编译器在Select as generic type参数中推断出ICoupon而不是Coupon 。 因此,在其他人提出的Select之后(由于它需要迭代所有项目而不太高效),您可以通过指定正确的Selectgenerics类型来使用隐式转换(或更正确的方差),而不是显式转换:

 public IList GetCouponsForSite(string siteSlug) { return _db.Coupons.Where(x => x.Site.slug == siteSlug) .Select(x => new Coupon(x.id)).ToList(); } 

(您需要使用相应类型的Coupons集合替换?