Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List of Interfaces vs. List of Derived Type - Cannot Convert Expression Type to Return Type

Tags:

c#

interface

linq

Why does this work:

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

But this does does not work (error - cannot convert expression type to return type):

public IList<ICoupon> GetCouponsForSite(string siteSlug)
{
    return _db.Coupons.Where(x => x.Site.slug == siteSlug)
                      .Select(x => new Coupon(x.id)).ToList();
}
like image 814
Martin Avatar asked Dec 08 '09 01:12

Martin


1 Answers

Because db.Coupons...ToList() returns an IList<Coupon> rather than an IList<ICoupon>. IList<Coupon> does not derive from IList<ICoupon> because C# 3 doesn't support generic variance. (C# 4 does support generic variance, but it still won't derive in this case. Consider that whoever receives an IList<ICoupon> could try to stuff a SomeEvilTypeThatImplementsICoupon into it. But an IList<Coupon> couldn't accept that because SomeEvilTypeThatImplementsICoupon doesn't derive from Coupon. See http://hestia.typepad.com/flatlander/2008/12/c-covariance-and-contravariance-by-example.html for discussion of this convertibility issue albeit in a slightly different context, and the Eric Lippert articles linked from there.)

(Your first snippet, by contrast, explicitly constructs a List<ICoupon>, which can contain anything that implements ICoupon, and then puts some Coupon objects into that list. Now if the receiver decides to poke SomeEvilTypeThatImplementsICoupon into it, all is well, because the List was built to hold any ICoupon, not just actual Coupon objects.)

like image 140
itowlson Avatar answered Oct 29 '22 06:10

itowlson