2009-12-08 91 views
6

为什么这项工作:的列表界面中对派生类型的列表 - 不能转换表达式类型到返回类型

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(); 
} 

回答

10

因为db.Coupons ... ToList()返回IList<Coupon>而不是IList<ICoupon>IList<Coupon>不是从IList<ICoupon>派生的,因为C#3不支持通用方差。 (C#4确实支持泛型差异,但在这种情况下它仍然不会产生,请考虑收到IList<ICoupon>的人可能会尝试填入SomeEvilTypeThatImplementsICoupon,但IList<Coupon>不能接受,因为SomeEvilTypeThatImplementsICoupon不是来自Coupon对于这个兑换问题,尽管在一个稍微不同的上下文中的讨论,参见http://hestia.typepad.com/flatlander/2008/12/c-covariance-and-contravariance-by-example.html,并从那里链接的埃里克利珀文章。)

(你的第一个片段,相比之下,明确构建一个List<ICoupon>,这可以包含任何然后将一些优惠券对象放入该列表中,现在如果接收方决定将SomeEvilTypeThatImplementsICoupon放入其中,则一切都很好,因为该列表是为了容纳任何ICoupon ,而不仅仅是实际的优惠券对象。)

0

IQueryable<ICoupon>不从IList<ICoupon>的。

+0

有一个.ToList()在那里...... – Martin 2009-12-08 01:49:18

+0

对不起,没看到。使返回语句'var x = _db ...;'然后'return x'。将鼠标悬停在“var x”上以查看VS认为它是什么类型。 – 2009-12-08 01:51:35

+0

无法将表达式类型列表转换为返回类型IList Martin 2009-12-08 01:54:56

4

它不能隐式投射列表<优惠券>到列表<ICoupon>。试试这个:

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>。因此,乍一看它应该能够使用一个作为另一个,这两种类型之间存在相当重要的区别。

演员调用基本上遍历列表,并为新列表中的每个列表进行类型转换(由于性能方面的原因,它有一点更多,但实际上您可以这样想)。

(更新与修复评论)

+0

Typo:_db.Coupons.Where(x => x.Site.slug == siteSlug).Select(x => new Coupon(x.id))。Cast ().ToList()...但是,谢谢,这有效。 – Martin 2009-12-08 02:09:17

+0

其中任何一个都应该可以工作 - Cast在IQueryable和IEnumerable接口上,因此您可以在转换为列表之前或之后进行强制转换。它没有什么区别。 – fyjham 2009-12-08 02:30:46

+0

.Cast <>()返回一个IEnumerable ...这就是为什么你需要使用.ToList()之后的.Cast <>() – Martin 2009-12-08 17:33:25

0

这是因为编译器推断ICoupon,而不是Coupon,在Select作为泛型类型参数。因此,而不是在Select后有明确的转换中所建议的人(这不是太有效,因为它需要遍历所有的项目),您还可以使用指定正确的Select generic types隐式转换(或更正确方差):

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

(您需要使用合适的类型的Coupons收集来代替?。)