2015-04-23 8 views
6

Lassen Sie uns sagen, dass ich eine generische Box Klasse haben wollen, dass etwas im Inneren enthalten kann, so ist es ein Box<T>. Box<T> hat eine Transform Methode, die eine Box<U> zurückgibt: das hatMerkwürdiger wiederkehrend Schablonenmuster mit zusätzlichen generischen Typen

public Box<U> Transform<U>(Func<T, U> transform) 

bisher ganz einfach. Allerdings benötige ich eigentlich eine Zusammenfassung Box, da die Art, wie die Werte eingerahmt und transformiert werden, implementierungsspezifisch ist. (Ich kann keine Schnittstelle haben, da es andere Methoden gibt, die durch die Komposition von abstrakten Methoden implementiert werden, aber das ändert sowieso nichts).

Natürlich möchte ich meine überschriebene Transform Methoden, um eine entsprechende Unterklasse von Box, nicht Box selbst zurückgeben. Da Rückgabetypen von überragender Methoden in # C invariant sind, wende ich mich an die curiously recurring template pattern (siehe IComparable<T>):

public abstract class Box<B, T> where B : Box<B, T> 

Nun sollte sich jede Klasse I von Box<B, T> erben beziehen oder bricht die Hölle los:

public class FooBox<T> : Box<FooBox, T> 

jedoch zerstört diese vollständig die Transform Methode:

public abstract Box<B, U> Transform<U>(Func<T, U> transform); 

schlägt mit 0 kompilieren. Das ist sinnvoll, da der Rückgabetyp jetzt Box<B, U> ist und B Box<B, T> ist, was nicht Box<B, U> ist.

Die einfache fix wird nicht funktionieren:

public abstract Box<B, U> Transform<U>(Func<T, U> transform) where B : Box<B, U>; 

nicht mit 'Test.Box<B,T>.Transform<U>()' does not define type parameter 'B' (CS0699) kompilieren.

Gibt es eine Möglichkeit, dies zu lösen, oder habe ich mich wirklich in eine Ecke gemalt?

+0

Können Sie machen es nur eine 'Box ' und Implementierung spezifischer Informationen in über Factory-Methoden übergeben? –

+0

@MillieSmith entfernen CRTP würde natürlich funktionieren, aber das würde dazu führen, dass nicht in der Lage sein, diese Methoden wie 'Box Merge (Box andere, Func Merge)' tatsächlich kompatible akzeptieren 'Box's. – Alexey

Antwort

1

Ich denke, das Problem mit der direkten Fix ist die Wiederverwendung der B Typ Parameter. Probieren Sie etwas anderes, und schließen es als Typ-Parameter:

public abstract Box<B2, U> Transform<B2,U>(Func<T, U> transform) where B2 : Box<B2, U>; 

Update: Sie erklärte:

jetzt ich nicht, dass die B2 garantieren kann und B sind eigentlich die gleiche abgeleitete Klasse, die mein Ziel war

Diese

ist nicht der Fall jedoch B2 nicht (nicht?) erben von B, U könnte T erben, wenn Sie es wünschen. Sie können das als Einschränkung einschließen. Das ist jedoch nicht unbedingt notwendig für das Muster, da es bis zum Körper von Transform, um es auszusortieren.

z:

public abstract Box<B2, U> Transform<B2,U>(Func<T, U> transform) 
    where B2 : Box<B2, U> 
    where U : T 
+0

Nicht 100%, dass Sie 'B2' anstelle von' B' brauchen, aber Sie brauchen es definitiv als Typparameter für 'Transform' –

+0

Das kompiliert (wenn ich B2 zu den generischen Parametern der Methode hinzufüge), aber jetzt kann ich das nicht garantieren B2 und B sind eigentlich die gleiche abgeleitete Klasse, was mein Ziel war. – Alexey

+0

aktualisierte Antwort. –