2012-04-15 12 views
5

mein Problem ist das folgende. Ich habe ein Code-Design für ein Heimprojekt gemacht, das anscheinend nicht funktioniert. Vielleicht kannst du mir helfen herauszufinden, woher der "Code-Geruch" kommt.Deklaration einer abstrakten generischen Variablen

Ok fangen wir an: Ich habe einige Klassen definiert, um verschiedene Art von Archivtypen wickeln:

public abstract class Archive { } 
public class ZipArchive : Archive { } 
public class TarArchive : Archive { } 

mit diesen Archiven umgehen, definiert ich Klassen-Manager. Ein abstraktes eines, das das benötigte Verhalten definiert,

public abstract class ArchiveManager<T> where T : Archive 
{ 
    public abstract void OpenArchive(T archive); 
} 

Und die konkret diejenigen, die tatsächlich die spezifische behaiour implementieren:

public class ZipArchiveManager : ArchiveManager<ZipArchive> 
{ 
    public override void OpenArchive(ZipArchive archive) { /* .. */ } 
} 

public class TarArchiveManager : ArchiveManager<TarArchive> 
{ 
    public override void OpenArchive(TarArchive archive) { /* .. */ } 
} 

Was jetzt passiert, ist, dass während der Kompilierung, ich weiß nicht, welche Art Archive werde ich verarbeiten, so habe ich versucht, die folgenden:

class Program 
{ 
    static void Main(string[] args) 
    { 
     ArchiveManager<Archive> archiveManager = null; 

     if (/*some condition*/) {    
      archiveManager = new ZipArchiveManager(); 
     } 
     else { 
      archiveManager = new TarArchiveManager(); 
     } 
    } 
} 

, die in dem folgenden Fehler endete:

Cannot implicitly convert type 'ZipArchiveManager' to 'ArchiveManager'

Soweit ich verstehe, kann das generische Argument nicht implizit konvertiert werden. Gibt es eine Möglichkeit, das zu umgehen? Ist dieser Code/Design "Geruch"?

Vielen Dank im Voraus.

+0

a erstellen nicht generische Basisklasse oder verwenden Sie eine kovariante Schnittstelle. – CodesInChaos

+1

Auch die Signatur von 'OpenArchive' scheint mir falsch zu sein. Sollte es nicht einen Strom erhalten und "T" zurückgeben? – CodesInChaos

Antwort

2

Sie können eine kontravariante Schnittstelle anstelle einer abstrakten Klasse verwenden, die keine Funktionalität implementiert. In diesem Fall können Sie nur die Typ-Parameter als Rückgabewert einer Methode verwenden, nicht als Argument:

public interface IArchiveManager<out T> 
    where T : Archive 
{ 
    T OpenArchive(Stream stream); 
} 

Dann implementieren Sie einfach die Schnittstelle in Ihren Manager-Klassen:

public class ZipArchiveManager : IArchiveManager<ZipArchive> 
{ 
    public ZipArchive OpenArchive(Stream stream) 
    { 
     // ... 
    } 
} 

public class TarArchiveManager : IArchiveManager<TarArchive> 
{ 
    public TarArchive OpenArchive(Stream stream) 
    { 
     // ... 
    } 
} 
+0

Vielen Dank für Ihre Antworten. Kontravariante Schnittstelle scheint die einzige Möglichkeit zu sein, dies zu überwinden. Ich glaube jedoch, dass ich meine Architektur als Ganzes überdenken muss ... :( – Flagg1980

0

I einen anderen Weg gefunden, indem die "dynamische" Schlüsselwort von C# .NET 4.0 ...

class Program 
{ 
    static void Main(string[] args) 
    { 
     dynamic archiveManager = null; 

     if (/*some condition*/) {    
      archiveManager = new ZipArchiveManager(); 
     } 
     else { 
      archiveManager = new TarArchiveManager(); 
     } 
    } 
} 

wirkt wie ein Zauber für mich;)

+0

Der Nachteil der Verwendung von 'dynamic' besteht darin, dass Sie die statische Typprüfung verlieren. Wenn Sie sich jemals dafür entscheiden, Ihre Methodennamen, Methodensignaturen oder Konstruktoren zu ändern, Der Compiler wird dies für "dynamische" Typen nicht abfangen. – Michael

Verwandte Themen