2009-12-08 18 views
6

Perché fa questo lavoro:elenco delle interfacce vs. Elenco di tipo derivato - Impossibile convertire Espressione Tipo di ritorno Tipo di

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

Ma questo non funziona (errore - non può convertire il tipo espressione al tipo di ritorno):

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

risposta

10

Perché db.Coupons ... ToList() restituisce un IList<Coupon> anziché IList<ICoupon>. IList<Coupon> non deriva da IList<ICoupon> poiché C# 3 non supporta la varianza generica. (C# 4 supporta la varianza generica, ma in questo caso non verrà derivata. Considera che chiunque riceva un IList<ICoupon> potrebbe tentare di inserire un SomeEvilTypeThatImplementsICoupon in esso, ma un IList<Coupon> non lo può accettare perché SomeEvilTypeThatImplementsICoupon non deriva dal Coupon . Vedere http://hestia.typepad.com/flatlander/2008/12/c-covariance-and-contravariance-by-example.html per la discussione di questo problema la convertibilità sia pure in un contesto leggermente diverso, e gli articoli Eric Lippert collegati da lì.)

(il tuo primo frammento, al contrario, costruisce in modo esplicito un List<ICoupon>, che può contenere tutto ciò che implementa ICoupon, quindi inserisce alcuni oggetti Coupon in quella lista. Ora se il destinatario decide di inserire SomeEvilTypeThatImplementsICoupon in esso, tutto va bene, perché l'Elenco è stato creato per contenere qualsiasi ICoupon , non solo oggetti promozionali effettivi.)

0

IQueryable<ICoupon> non deriva da IList<ICoupon>.

+0

c'è un ToList() in là ... – Martin

+0

Siamo spiacenti, non ha visto questo. Rendi la dichiarazione di ritorno 'var x = _db ...;' quindi 'restituisci x'. Passa il mouse sopra 'var x' per vedere che tipo VS pensa che sia. –

+0

impossibile convertire il tipo di espressione Elenco per restituire il tipo IList Martin

4

non può implicitamente lanciare Lista <Coupon> to List <ICoupon>. Prova questo:

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 ragione fondamentale di questo è che se si ha ad esempio un class FancyCoupon : ICoupon e ha cercato di mettere che in un List<Coupon> allora fallirebbe causa FancyCoupon non deriva da Cedola (solo ICoupon) ma dovrebbe funzionare bene in un List<ICoupon>. Quindi, a prima vista sembra che dovrebbe essere in grado di usarne uno come l'altro ci sono differenze piuttosto importanti tra i due tipi.

La chiamata al cast essenzialmente scorre l'elenco e si sintonizza ciascuno per il nuovo elenco (C'è un po 'di più per motivi di prestazioni sotto il cofano, ma per scopi pratici si può pensare in questo modo).

(aggiornata con correzione dal commento)

+0

Typo: _db.Coupons.Where (x => x.Site.slug == siteSlug) .Seleziona (x => nuovo Coupon (x.id)). Cast () .ToList() ... ma grazie, funziona. – Martin

+0

Uno di questi dovrebbe funzionare - Il cast è su entrambe le interfacce IQueryable e IEnumerable in modo da poter eseguire il cast prima o dopo la conversione in un elenco. Fa poca differenza. – fyjham

+0

.Cast <>() restituisce un oggetto IEnumerable ... ecco perché è necessario utilizzare .ToList() dopo .Cast <>() – Martin

0

Questo perché il compilatore deduce ICoupon, e non Coupon, nel Select come argomento di tipo generico. Così, invece di un cast esplicito dopo l'Select come proposto da altri (che non troppo efficiente perché ha bisogno di iterare su tutti gli elementi), è possibile utilizzare anche il cast implicito (o più correttamente varianza) specificando la corretta 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(); 
} 

(è necessario sostituire il ? con il tipo appropriato di raccolta Coupons.)

Problemi correlati