2009-12-08 22 views
6

Por qué funciona esto:lista de interfaces vs Lista de tipo derivado - No se puede convertir tipo de expresión para devolver Tipo

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 puede convertir el tipo de expresión para volver tipo):

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

Respuesta

10

Porque db.Coupons ... ToList() devuelve IList<Coupon> en lugar de IList<ICoupon>. IList<Coupon> no deriva de IList<ICoupon> porque C# 3 no es compatible con 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 deriva de Coupon . Ver http://hestia.typepad.com/flatlander/2008/12/c-covariance-and-contravariance-by-example.html para la discusión de este aspecto de la convertibilidad aunque en un contexto algo diferente, y los artículos de Eric Lippert vinculados a partir de ahí.)

(Su primer fragmento, por el contrario, construye explícitamente un List<ICoupon>, que puede contener nada que implementa ICoupon, y luego coloca algunos objetos de Cupón 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 sólo los objetos promocionales reales.)

0

IQueryable<ICoupon> no se deriva de IList<ICoupon>.

+0

hay una .ToList() en allí ... – Martin

+0

lo sentimos, no vio eso. Haga la declaración return 'var x = _db ...;' then 'return x'. Desplácese sobre 'var x' para ver qué tipo de VS cree que es. –

+0

no se puede convertir el tipo de expresión Lista para devolver el tipo IList Martin

4

implícitamente no pueden lanzar Lista < Cupón > a la lista <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 fundamental de esto es que si usted tuviera por ejemplo un class FancyCoupon : ICoupon y trató de poner esto en un List<Coupon> entonces fallaría causa FancyCoupon no deriva de cupón (sólo ICoupon) pero debería funcionar bien en un 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 debajo del capó, pero para fines prácticos se puede pensar de esa manera).

(actualizado con arreglo a los comentarios)

+0

Typo: _db.Coupons.Where (x => x.Site.slug == siteSlug) .Seleccione (x => nuevo Cupón (x.id)). Cast () .ToList() ... pero gracias, eso funciona. – Martin

+0

Cualquiera de estos debería funcionar: Cast está en las interfaces IQueryable e IEnumerable para que pueda convertir antes o después de convertir a una lista. Hace poca diferencia. – fyjham

+0

.Cast <>() devuelve un IEnumerable ... es por eso que necesita utilizar el .ToList() después del .Cast <>() – Martin

0

Esto es porque el compilador infiere ICoupon, y no Coupon, en el Select como argumento de tipo genérico. Así que en lugar de una conversión explícita después de la Select según lo propuesto por otros (que no muy eficiente, ya que necesita para iterar sobre todos los elementos), también se puede utilizar la conversión implícita (o más correctamente varianza) especificando la correcta 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(); 
} 

(es necesario sustituir el ? con el tipo apropiado de la colección Coupons.)

Cuestiones relacionadas