Список интерфейсов и список производного типа - невозможно преобразовать тип выражения в тип возвращаемого значения - PullRequest
6 голосов
/ 08 декабря 2009

Почему это работает:

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

Ответы [ 4 ]

10 голосов
/ 08 декабря 2009

Потому что db.Coupons ... ToList () возвращает IList<Coupon>, а не IList<ICoupon>. IList<Coupon> не является производным от IList<ICoupon>, потому что C # 3 не поддерживает общую дисперсию. (C # 4 действительно поддерживает общую дисперсию, но она все равно не получится в этом случае. Учтите, что тот, кто получает IList<ICoupon>, может попытаться вставить в него SomeEvilTypeThatImplementsICoupon. Но IList<Coupon> не может принять это, поскольку SomeEvilTypeThatImplementsICoupon не делает t вытекает из Coupon. См. http://hestia.typepad.com/flatlander/2008/12/c-covariance-and-contravariance-by-example.html для обсуждения этой проблемы конвертируемости, хотя и в несколько ином контексте, и статей Эрика Липперта, связанных оттуда.)

(Ваш первый фрагмент, напротив, явно создает List<ICoupon>, который может содержать все, что реализует ICoupon, а затем помещает некоторые объекты Coupon в этот список. Теперь, если получатель решит ткнуть SomeEvilTypeThatImplementsICoupon в нем все хорошо, потому что List был создан для хранения любого ICoupon, а не только реальных объектов Coupon.)

4 голосов
/ 08 декабря 2009

Он не может неявно привести Список в Список . Попробуйте это:

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 не наследуется от Coupon (только ICoupon), но он должен работать только штраф в List<ICoupon>. Таким образом, хотя на первый взгляд кажется, что он должен быть в состоянии использовать один, а другой, между этими двумя типами есть довольно важные различия.

Вызов приведения по существу перебирает список и настраивает каждый из них для нового списка (из соображений производительности под капотом есть кое-что еще, но для практических целей вы можете думать об этом так).

(Обновлено с исправлением из комментариев)

0 голосов
/ 09 декабря 2009

Это потому, что компилятор выводит ICoupon, а не Coupon, в Select в качестве аргумента универсального типа. Таким образом, вместо явного приведения после Select, как это было предложено другими (что не слишком эффективно, потому что для этого необходимо выполнять итерации по всем элементам), вы также можете использовать неявное приведение (или, вернее, дисперсию), указав правильный Select универсальные типы :

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

(Вам необходимо заменить ? на соответствующий тип коллекции Coupons.)

0 голосов
/ 08 декабря 2009

IQueryable<ICoupon> не является производным от IList<ICoupon>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...