2009-12-08 3 views
6

Warum funktioniert das:Liste der Interfaces vs. Liste der abgeleiteten Typ - nicht Ausdrucksart Typ konvertieren kann Zurückgeben

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

Aber das funktioniert nicht (Fehler - nicht Ausdrucksart Typ zurückzukehren umwandeln kann):

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

Antwort

10

Weil db.Coupons ... ToList() einen IList<Coupon> statt IList<ICoupon> zurückgibt. IList<Coupon> leitet sich nicht von IList<ICoupon> ab, da C# 3 generische Varianz nicht unterstützt. (C# 4 unterstützt generische Varianz, wird aber in diesem Fall immer noch nicht abgeleitet. Beachte, dass wer auch immer eine IList<ICoupon> empfängt, könnte versuchen, ein SomeEvilTypeThatImplementsICoupon hineinzustopfen. Aber eine IList<Coupon> konnte das nicht akzeptieren, weil SomeEvilTypeThatImplementsICoupon nicht von Coupon abgeleitet ist Siehe. http://hestia.typepad.com/flatlander/2008/12/c-covariance-and-contravariance-by-example.html zur Diskussion dieser Konvertibilität Problem, wenn auch in einem etwas anderen Kontext und die Eric Lippert Artikel von dort verlinkt sind.)

(Ihr erster Schnipsel, dagegen explizit ein List<ICoupon> Konstrukten, die kann alles enthalten, was implementiert ICoupon und fügt dann einige Coupon-Objekte in diese Liste ein.Wenn der Empfänger nun entscheidet, SomeEvilTypeThatImplementsICoupon hineinzuschieben, ist alles in Ordnung, weil die Liste so aufgebaut wurde, dass sie einen ICoupon enthält , Nicht nur aktuelle Coupon-Objekte.)

0

IQueryable<ICoupon> nicht von IList<ICoupon> abgeleitet.

+0

Es gibt eine .ToList() in dort ... – Martin

+0

Sorry, das nicht gesehen haben. Machen Sie die return-Anweisung 'var x = _db ...;' und dann 'return x'. Bewegen Sie den Mauszeiger über das 'var x', um zu sehen, welcher Typ VS dies ist. –

+0

kann nicht Ausdrucksart Liste konvertieren Typ IList Martin

4

Es kann nicht implizit Besetzungsliste <Coupon> <iCoupon> zur Liste. Versuchen Sie folgendes:

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

Der wesentliche Grund dafür ist, dass, wenn Sie zum Beispiel hat ein class FancyCoupon : ICoupon und versuchte, das in ein List<Coupon> zu setzen, dann wäre es nicht Ursache FancyCoupon nicht von Coupon nicht ableiten (nur iCoupon), aber es sollte gut in eine List<ICoupon> funktionieren. Während es auf den ersten Blick aussieht, als ob es einen als den anderen verwenden könnte, gibt es ziemlich wichtige Unterschiede zwischen den beiden Typen.

Der Cast-Aufruf iteriert im Wesentlichen über die Liste und schreibt jeden für die neue Liste (Es gibt ein bisschen mehr aus Performance-Gründen unter der Motorhaube, aber aus praktischen Gründen kann man es sich so vorstellen).

(aktualisiert mit fix von Kommentaren)

+0

Typo zurückzukehren. _db.Coupons.Where (x => x.Site.slug == siteSlug) .Select (x => neue Coupon (x.id)) Cast () .ToList() ... aber danke, das funktioniert. – Martin

+0

Einer davon sollte funktionieren - Cast befindet sich sowohl auf der IQueryable- als auch auf der IEnumerable-Schnittstelle, sodass Sie vor oder nach dem Konvertieren in eine Liste konvertieren können. Es macht wenig Unterschied. – fyjham

+0

.Cast <>() gibt einen IEnumerable zurück ... deshalb müssen Sie die .ToList() nach dem .Cast <>() verwenden – Martin

0

Dies ist, weil der Compiler ICoupon folgert, und nicht Coupon im Select als generische Typargument. Anstatt also eine explizite Umwandlung nach dem Select wie von anderen vorgeschlagen (die nicht zu effizient, weil es alle Elemente iterieren muss), können Sie auch implizites Casting (oder richtiger Varianz) durch die richtige Select generic types Angabe:

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

(Sie müssen die ? mit dem entsprechenden Typ der Coupons Sammlung ersetzen.)

Verwandte Themen