2016-08-03 4 views
1

ich bin einige meiner Java-Code in C# zu portieren, und ich habe Probleme beim Replizieren dieses Verhalten:Shop generischer Typ Referenz in C#

***** Java-Code *****

public abstract class Fruit<T extends Fruit>{ 
    //Fruit implementation 
} 

Das ist großartig, weil ich nur generische Typen, die Obst erweitern möchten. Dann kann ich eine Referenz für alle konkreten Fruchtobjekte wie folgt speichern:

Banana banana = new Banana(); //This class extends Fruit 
Strawberry strawberry = new Strawberry(); //This class extends Fruit 

Fruit fruit; 
fruit = banana; 
//or 
fruit = strawberry; 

Das funktioniert ganz gut. Jetzt versuche ich, das gleiche in C# und die Frucht-Klasse wird wie folgt erklärt:

***** C# -Code *****

abstract public class Fruit<T> where T : Fruit<T> { 
    //Fruit implementation 
} 

Aber in C#, kann ich nicht speichern einer Referenz wie folgt aus:

Fruit fruit; //This gives a compilation error! 

ich nicht in der Lage bin, die Banane und die Erdbeere in der gleichen Referenz zu speichern, kann ich dies nur tun:

Fruit<Banana> fruit; 
fruit = banana; 
//or 
Fruit<Strawberry> fruit; 
fruit = strawberry; 

glaube ich um es durch Zugabe einer Vererbungsstufe wie folgt zu erreichen:

abstract public class GenericFruit<T> where T : GenericFruit<T> {} 

und erstellen Sie dann die Frucht Klasse von Obst wie diese äquivalent

abstract public class Fruit : GenericFruit<Fruit>{} 

und jetzt Banane und Erdbeer erstreckt:

public class Banana : Fruit {} 
public class Strawberry : Fruit {} 

und speichern Sie dann eine Frucht Referenz:

Fruit fruit; 
fruit = new Banana(); 
fruit = new Strawberry(); 

Aber das fühlt sich irgendwie wie Betrug an :( Irgendwelche Ideen? Mache ich etwas falsch?

+0

Warum hast du '' nur 'public abstract class Fruit hinzugefügt {...} kann genau dasselbe! versuchen Sie es – Maraboc

+0

Hey @Maraboc, ich vergaß zu erwähnen, dass innerhalb der Fruit-Implementierung ich generische Rückgabetypen verwenden ... – loveMeansNothing

+0

Ich bin nicht einmal sicher, was Sie versuchen, in den C# -Code zu erreichen. Sie erhalten den Kompilierungsfehler, weil Sie die Frucht auf einen Typ beschränkt haben und dann keinen Typ angeben. –

Antwort

3

Das Problem, das Sie in Laufen sind ist, dass Sie versuchen, einige der von Ihnen erstellten Informationen zu "vergessen" (oder zu löschen). Nehmen wir das Beispiel ein wenig konkreter machen, indem ein Verfahren zu Ihrer Basisklasse hinzu:

public abstract class Fruit<T> where T : Fruit<T> 
{ 
    public abstract T GetSeedless(); 
} 

Okay, die mehr jetzt schauen wir uns genau an, was Sie versuchen zu tun. Nehmen wir an, dass Sie genau das tun konnte, was man wollte und man hatte einen Obstkorb:

Fruit fruit = new Apple(); 
var seedlessFruit = fruit.GetSeedless(); 

Okay, was ist die Art von seedlessFruit? Sie könnten geneigt sein zu sagen, dass es Fruit ist und das wäre vernünftig, aber C# lässt dies nicht zu. C# lässt Sie den generischen Parameter einer Klasse nicht löschen. Wenn Sie Fruit<T> deklarierten Sie alle Fruit, um einen generischen Parameter zu haben, können Sie das nicht löschen.

Ich denke, Sie sind nah an einer Lösung, aber ich denke, Sie haben es ein bisschen auf den Kopf gestellt. Anstatt das nicht-generische Fruit von GenericFruit<Fruit> zu erben, sollten Sie es spiegeln und die generische Version von der nicht-generischen Version erben lassen.

Ich habe auch eine andere Empfehlung und das ist die nicht-generische Fruit in eine Schnittstelle statt eine abstrakte Klasse zu machen. Ich werde zeigen, warum (schließlich ist es, weil C# Rückkehr Typ Kovarianz nicht erlaubt, wenn Methoden überschreiben; ein Mist, um sicher zu sein).

public interface IFruit 
{ 
    IFruit GetSeedless(); 
} 

public abstract class Fruit<T> : IFruit where T : Fruit<T> 
{ 
    public abstract T GetSeedless(); 

    IFruit IFruit.GetSeedless() 
    { 
     return GetSeedless(); 
    } 
} 

Was ich hier getan ist eine Art erstellt von einer gefälschten Kovarianz Rückgabetyp explizit die IFruit Schnittstelle in der Fruit Klasse implementieren. Dies ermöglicht es Ihnen jetzt verschiedene Arten von Früchten in der gleichen Referenz zu speichern und immer noch die GetSeedless Methode verwenden:

Auf diese Weise können Sie auch auswählen und welche Methoden und Eigenschaften wählen zur Verfügung stehen sollten, wenn Sie die generische löschen möchten Information. Jede dieser Methoden kann explizit in der Basisklasse implementiert und durch generische Versionen "ersetzt" werden. Auf diese Weise können Sie, wenn Sie über die generischen Informationen verfügen, mit spezifischeren Typen arbeiten.

+0

Die Schnittstelle Idee war großartig! Ich ging mit dieser Option, danke :) – loveMeansNothing

2

Vor allem dies:

abstract public class Fruit<T> where T : Fruit<T> 

einfach nicht funktionieren kann, weil Sie eine Endlosschleife schaffen, indem er sagte TFruit<T> ist. (Beginnen Sie zu ersetzen T in Fruit<T> von Fruit<T>, Sie werden sehen, es ist unmöglich zu beenden).

EDIT: Wie Kyle sagte, das funktioniert.

könnte die Lösung sein:

abstract public class Fruit 
{ 
    // Generic implementation 
} 

abstract public class Fruit<T> : Fruit 
    where T : Fruit // edit: or Fruit<T> 
{ 
    // Overriding generic implementation 
} 

Und man konnte haben:

public class Banana : Fruit<YourType> // edit: or Fruit<Banana> 
{ 
    // Specific implementation 
} 

Schließlich sollte dies gut funktionieren:

Fruit fruit; 
fruit = new Banana(); 
fruit = new Strawberry(); 
+0

Die erste Aussage ist einfach falsch.Dieses wiederkehrende Vorlagenmuster ist in C# vollkommen in Ordnung und wird gut kompiliert und führt zu keiner Art von unendlichem Rückschritt im Typsystem. Berücksichtigen Sie 'öffentliche Klasse Banane: Frucht '. 'Banana' erfüllt die generische Einschränkung. Dieses wiederkehrende Generic wird kompilieren und gut funktionieren. – Kyle

+0

Danke für den Kommentar, das wusste ich nicht. :) –

+0

Keine Sorgen. Es ist eine der ungewöhnlicheren Anwendungen von Generika und es ist nicht ganz offensichtlich, dass es * funktionieren * sollte. Aber trotzdem tut es. – Kyle