2008-11-11 14 views
10

In C# wünschte ich manchmal, ich könnte spezielle Methoden für bestimmte "Instanziierungen" von generischen Klassen machen.Muster für die Spezialisierung der generischen Klasse in C#?

UPDATE: Der folgende Code ist nur ein dummes Beispiel für ein abstrakteres Problem - konzentrieren Sie sich nicht zu sehr auf Zeitreihen, nur die Grundsätze der "zusätzliche Methoden hinzufügen" für bestimmte T.

Beispiel:

class Timeseries<T> 
{ 
    ... 
    TimeSeries<T> Slice(...) { ... } 
} 

In dem Fall, wo T doppelt so hoch ist, würde ich einige zusätzliche Methoden mag, wie Integrate(), Interpolate() und so weiter, dass nur dann einen Sinn für double machen, weil ich auf sich Arithmetik tun muß .

Es gibt mehrere Möglichkeiten, dies zu tun, aber ich kann keine finden, mit der ich zufrieden bin.

1. Vererben in eine Sonderklasse

class TimeseriesDouble : Timeseries<double> 
{ 
    double Interpolate(...) { ... } 
    ... 
} 

Nachteile:TimeseriesDouble.Slice() wird ein neues Timeseries<double> Objekt zurück, jetzt meine spezielle Methoden fehlt.

2. Externe Methoden

public static double Interpolate(Timeseries<double> ts, ...) { ... } 

Nachteile: Breaks mit OO Prinzipien. Und ich möchte meine Methoden nicht wegräumen. Außerdem benötigen die Methoden möglicherweise einen privaten/geschützten Status.

3. Erweiterungsmethoden

Wie 2, nur mit einem schöneren Aufruf Syntax.

4. Gemeinsame Basisklasse

class TimeSeries_base { ... } 
class TimeSeries<T> : TimeSeries_base { .. typesafe versions of methods .. } 
class TimeSeriesDouble : TimeSeries_base { .. typesafe versions of methods .. } 

Nachteile: zu viel Verdoppelung der Dinge aus TimeSeries_base in die beiden Unterklassen. Die Basisklasse könnte nur ein Platzhalter für Utility-Funktionen für die Unterklassen werden.

pro: Ich kann jetzt Dinge wie List<TimeSeries_base> dynamisch tun.

5. Vergessen Sie um eine gemeinsame Klasse

Das heißt, halten Timeseries<T> und TimeseriesDouble im Code getrennt.

Nachteile: Dann alles, was ich nicht bekommen, den Nutzen einer solchen TimeseriesDouble wie ein TimeSeries<T>, beispielsweise die Behandlung von Kombinieren von zwei Zeitreihen mit ZIP (A, B), wobei es sich um Doppelgänger handelt.


Irgendwelche anderen Ideen? Momentan finde ich das Design (1) am besten.

+0

Es könnte nützlich sein, konkretere Beispiele für den Clientcode zu geben, den Sie haben, und was eine TimeSeries ist. Wenn Sie dies nicht tun, erhalten Sie eine Menge nicht-themenbezogener Antworten, weil es nicht genug Informationen gibt, um weiterzumachen, und die Leute werden Vermutungen anstellen. –

+0

zum Beispiel, ich verstehe nicht, warum (2) TimeSeries nicht zurückgeben kann, oder wenn das gültig ist, warum kann ich keine öffentliche statische T Interpolieren (Timeseries ts, ...) {...}, etc. Kann ich eine TimeSeries haben? –

+0

Ja, vielleicht sollten die Beispiele konkreter sein. Aber ich denke, dies ist nur ein Beispiel für ein allgemeineres Design-Problem. Vielleicht sollte ich das Problem abstrakter ausdrücken, damit TimeSeries nicht von der Sache ablenkt? –

Antwort

12

Sie könnten immer die selbstbezüglicher Generika Trick:

public class TimeSeries<T, U> where U : TimeSeries<T, U> 
{ 
    U Slice(...) 
} 

public class TimeSeriesDouble : TimeSeries<double, TimeSeriesDouble> 
{ 
    ... 
} 

Es kann ein bisschen kniffliger, aber es kann funktionieren.

+0

In meiner konkreten Bibliothek denke ich, dass ich etwas geradliniger gehen werde. Netter Trick, aber ich behalte es in meiner Tasche :-) –

+0

aber es würde immer noch ein Problem geben, wenn Sie 'TimeSeriesDouble' erweitern möchten. Dann würde die "Slice" -Methode der Unterklasse ein "TimeSeriesDouble" und keine Instanz des Subtyps zurückgeben. Ich würde F # und Musterabgleich für die Handhabung dieser Art von Strukturen verwenden. – SRKX

0

Ich würde in Erwägung ziehen, eine Fabrik zu implementieren, um die entsprechende Unterklasse zurückzugeben.

1

Ich würde mit (1) gehen und entsprechend werfen.

TimeSeriesDouble tsD = new TimeSeriesDouble(); 
TimeSeriesDouble subTSD = tsD.Slice(...) as TimeSeriesDouble; 
+0

Ja, ich mag eine solche Lösung. Vorbehalt: .Slice() (eine Methode in TimeSeries ) muss jetzt tatsächlich ein TimeSeriesDouble zurückgeben, also muss es virtuell sein oder andere Tricks verwenden. –

9
interface ITimeSeries<T> { ... } 

abstract class TimeSeriesBase<TS> where TS : TimeSeriesBase<TS> 
{ public TS Slice() { ... } 
} 

class TimeSeries<T>:TimeSeriesBase<TimeSeries<T>>,ITimeSeries<T> {} 

class TimeSeriesDouble:TimeSeriesBase<TimeSeriesDouble>,ITimeSeries<double> 
{ public double Interpolate() { ... } 
} 
+0

Perfekt! Genau das habe ich mir gedacht. –

+0

Das ist das Muster, das ich auch verwende. –

+0

Welche Vorteile bietet die Verwendung dieses Ansatzes anstelle dieses von Jon Skeet veröffentlichten Ansatzes? Es ist fast das Gleiche, oder? – SOReader

Verwandte Themen