2013-03-24 6 views
7

Ich versuche, eine Methode zu erstellen, die eine Liste des beliebigen Typs zurückgibt, den der Benutzer möchte. Um dies zu tun, verwende ich Generika, mit denen ich nicht so vertraut bin, also kann diese Frage offensichtlich sein. Das Problem ist, dass dieser Code nicht und wirft die Fehlermeldung Cannot convert type Systems.Collections.Generic.List<CatalogueLibrary.Categories.Brand> to Systems.Collection.Generic.List<T>Kann IList verwenden, aber nicht List in generischer Methode

private List<T> ConvertToList<T>(Category cat) 
{    
    switch (cat) 
    { 
     case Category.Brands: 
      return (List<T>)collection.Brands.ToList<Brand>(); 

    } 
    ... 
} 

funktioniert Aber wenn ich IList stattdessen verwenden, gibt es keine Fehler.

private IList<T> ConvertToList<T>(Category cat) 
{    
    switch (cat) 
    { 
     case Category.Brands: 
      return (IList<T>)collection.Brands.ToList<Brand>(); 

    } 
    ... 
} 

Warum kann ich in diesem Fall IList aber nicht List verwenden? collection.Brands gibt einen BrandCollection Typ aus einer Third-Party-Bibliothek zurück, so dass ich nicht weiß, wie das erstellt wird. Könnte es sein, dass BrandCollection von IList abgeleitet werden kann (nur raten, dass es tut) und so kann es in es konvertiert werden, aber nicht in eine normale Liste?

+0

Sie sollten Typengpässe dort passen! Du kannst nicht einfach so herumstochern, um Zeug zu machen. – antonijn

+3

Sind Sie sicher, dass Ihr zweites Beispiel nicht zurückgegeben wird (IList ) collection.Brands.ToList (); '? – Matthew

+0

Ich wollte sehen, ob das funktioniert hat. Ich bin ein wenig neu bei Generika, dachte mir, dass ich das bis später lassen würde, falls es etwas anderes kaputt machen würde: D Wäre der Zwang "wo T: Brand" (und die anderen Kategorien)? – XSL

Antwort

9

Da T keine Einschränkungen enthält, kann er nur zur Kompilierzeit in object konvertiert werden. Umwandlungen in Schnittstellentypen werden vom Compiler nicht überprüft, da theoretisch eine neue Klasse erstellt werden könnte, die IList<object> implementiert und List<Brand> erbt. Die Umwandlung in List<T> wird jedoch fehlschlagen, da bekannt ist, dass keine Klasse erstellt werden kann, die sowohl List<object> als auch List<Brand> erbt. In Ihrem Fall wissen Sie jedoch, was der Typ T durch Ihre switch Anweisung ist und möchten den Cast erzwingen. Um dies zu tun, werfen durch object zuerst wie folgt:

private List<T> ConvertToList<T>(Category cat) 
{    
    switch (cat) 
    { 
     case Category.Brands: 
      return (List<T>)(object)collection.Brands.ToList<Brand>(); 
    } 
} 

Das größere Design-Problem hier ist allerdings, dass Generika sind nicht die beste Wahl, wenn Sie eine diskrete Liste der bekannten Typen haben für T. Generika sind besser, wenn T entweder alles sein kann oder auf einen Basistyp oder eine Schnittstelle beschränkt sein kann. Hier würden Sie besser dran, nur ein separates Verfahren für jeden Zweig der switch-Anweisung zu schreiben:

private List<Brand> ConvertToBrandList() 
{ 
    return collection.Brands.ToList<Brand>(); 
} 

Ohne diese Sie haben sehr wenig Art Sicherheit. Was ist, wenn jemand Ihre Methode mit ConvertToList<int>(Category.Brands) aufruft?

+0

Dank für die Erklärung verwenden können. Obwohl die "Objekt" -Methode funktioniert hat, gehe ich auf die individuelle Methodenroute. Ich wäre nicht in der Lage, mich auf "Brand" zu beschränken, da ich gerade herausfand, dass es eine versiegelte Klasse ist. – XSL

+0

+1. Ich denke, der wahre Grund ist auf C# -Referenz 6.2.4 zurückzuführen: "Die expliziten Referenz-Conversions sind ...\t Von * jedem Klasse-Typ S * zu * jedem Interface-Typ T *, vorausgesetzt S ist nicht versiegelt und vorausgesetzt, S implementiert T nicht. " –