接口列表与派生类型列表 – 无法将表达式类型转换为返回类型
为什么这样做:
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
而不是IList
。 IList
不是来自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
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
之后(由于它需要迭代所有项目而不太高效),您可以通过指定正确的Select
generics类型来使用隐式转换(或更正确的方差),而不是显式转换:
public IList GetCouponsForSite(string siteSlug) { return _db.Coupons.Where(x => x.Site.slug == siteSlug) .Select, ICoupon>(x => new Coupon(x.id)).ToList(); }
(您需要使用相应类型的Coupons
集合替换?
)