c# - inside - iqueryable vs ienumerable
Lista de interfaces frente a lista de tipo derivado: no se puede convertir el tipo de expresión al tipo de devolución (4)
Por qué funciona esto:
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;
}
Pero esto no funciona (error: no se puede convertir el tipo de expresión para devolver el tipo):
public IList<ICoupon> GetCouponsForSite(string siteSlug)
{
return _db.Coupons.Where(x => x.Site.slug == siteSlug)
.Select(x => new Coupon(x.id)).ToList();
}
Esto se debe a que el compilador infiere ICoupon
, y no Coupon
, en el argumento Select
como tipo genérico. Por lo tanto, en lugar de un lanzamiento explícito después de Select
como propuesto por otros (que no es demasiado eficiente porque necesita iterar sobre todos los elementos), también puede usar el casting implícito (o varianza más correcta) especificando los tipos genéricos Select
correctos:
public IList<ICoupon> GetCouponsForSite(string siteSlug)
{
return _db.Coupons.Where(x => x.Site.slug == siteSlug)
.Select<?, ICoupon>(x => new Coupon(x.id)).ToList();
}
(Debe reemplazar el ?
Con el tipo apropiado de la colección de Coupons
).
No puede lanzar implícitamente List <Coupon> a List <ICoupon>. Prueba esto:
public IList<ICoupon> GetCouponsForSite(string siteSlug)
{
return _db.Coupons.Where(x => x.Site.slug == siteSlug)
.Select(x => new Coupon(x.id)).Cast<ICoupon>().ToList();
}
La razón básica para esto es que si tuvieras por ejemplo una class FancyCoupon : ICoupon
e intentaras poner eso en una List<Coupon>
entonces fallaría porque FancyCoupon no deriva de Coupon (solo ICoupon) pero debería funcionar bien en una List<ICoupon>
. Entonces, aunque a primera vista parece que debería poder usar uno como el otro, existen diferencias bastante importantes entre los dos tipos.
La llamada al elenco esencialmente itera sobre la lista y los pronuncia cada uno para la nueva lista (hay un poco más por motivos de rendimiento bajo el capó, pero a efectos prácticos se puede pensar de esa manera).
(Actualizado con la corrección de los comentarios)
Porque db.Coupons ... ToList () devuelve un IList<Coupon>
lugar de un IList<ICoupon>
. IList<Coupon>
no se deriva de IList<ICoupon>
porque C # 3 no admite la varianza genérica. (C # 4 admite varianza genérica, pero aún no se derivará en este caso. Considere que quien reciba un IList<ICoupon>
podría intentar rellenar SomeEvilTypeThatImplementsICoupon en él. Pero un IList<Coupon>
no podría aceptar eso porque SomeEvilTypeThatImplementsICoupon no se deriva de Cupón. Consulte http://hestia.typepad.com/flatlander/2008/12/c-covariability-and-contravariance-by-example.html para la discusión de este problema de convertibilidad, aunque en un contexto ligeramente diferente, y los artículos de Eric Lippert vinculados desde allí).
(Su primer fragmento, por el contrario, construye explícitamente un List<ICoupon>
, que puede contener cualquier cosa que implemente ICoupon, y luego coloca algunos objetos Coupon en esa lista. Ahora, si el receptor decide introducir SomeEvilTypeThatImplementsICoupon en él, todo está bien, porque la Lista se creó para contener cualquier ICoupon, no solo los objetos reales de Cupón).
IQueryable<ICoupon>
no se deriva de IList<ICoupon>
.