2008-11-11 14 views
10

En C# A veces me gustaría poder hacer métodos especiales para ciertas "instancias" de clases genéricas.¿Patrón para la especialización de la clase genérica en C#?

ACTUALIZACIÓN: El siguiente código es solo un tonto ejemplo de un problema más abstracto: no se centre demasiado en series de tiempo, solo en los principios de "agregar métodos adicionales" para ciertos T.

Ejemplo:

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

En el caso donde T es el doble, me gustaría algunos métodos adicionales, como Integrate(), Interpolate() y así sucesivamente que sólo tienen sentido para double, porque necesito hacer operaciones aritméticas sobre ellas .

Existen varias formas de hacerlo, pero no puedo encontrar una con la que esté satisfecho.

1. Heredar en una clase especial

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

contras:TimeseriesDouble.Slice() devolverá un nuevo objeto Timeseries<double>, ahora falta mis métodos especiales.

2. Métodos externos

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

contras: Rompe con los principios OO. Y no quiero apartar mis métodos. Además, los métodos pueden necesitar estado privado/protegido.

3. Los métodos de extensión

Igual que 2, sólo con una sintaxis llamar más agradable.

4. clase base común

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

contras: demasiada duplicación de cosas de TimeSeries_base en las dos subclases. La clase base podría convertirse simplemente en un marcador de posición para las funciones de utilidad para las subclases.

pro: Ahora puedo hacer cosas como List<TimeSeries_base> dinámicamente.

5. Olvídate de una clase común

es decir, mantener Timeseries<T> y TimeseriesDouble separada en el código.

contras: entonces no obtener todos los beneficios del tratamiento de una TimeseriesDouble como un TimeSeries<T>, por ejemplo, combinando dos timeseries con ZIP (A, B), donde uno resulta ser de dobles.


¿Alguna otra idea? Actualmente, creo que me gusta el diseño (1) mejor.

+0

Puede ser útil dar ejemplos más concretos del código de cliente que tiene, así como lo que es un TimeSeries. Si no lo hace, obtendrá muchas respuestas fuera de tema porque no hay suficiente información para seguir y la gente hará suposiciones. –

+0

por ejemplo, no entiendo por qué (2) no puede devolver TimeSeries , o si eso es válido, por qué no puedo tener T interpolar estático público (Timeseries ts, ...) {...}, ¿Puedo tener un TimeSeries ? –

+0

Sí, tal vez los ejemplos deberían ser más concretos. Pero pienso en esto como solo un ejemplo de un problema de diseño más general. Entonces, ¿debería expresar el problema de manera más abstracta, para que TimeSeries no se desvíe de la esencia? –

Respuesta

12

Siempre se puede utilizar el genéricos autorreferencial truco:

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

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

Se puede conseguir un poco de flexión de cerebro, pero puede funcionar.

+0

En mi biblioteca de concreto, creo que iré por algo más directo. Buen truco, sin embargo, lo guardaré en mi bolso :-) –

+0

, pero todavía habría un problema si quieres extender 'TimeSeriesDouble'. Entonces, la subclase '' Slice' method devolvería un 'TimeSeriesDouble' y no una instancia del subtipo. Utilizaría F # y la coincidencia de patrones para manejar este tipo de estructuras. – SRKX

0

Consideraría implementar una fábrica para devolver la subclase adecuada.

1

Me gustaría ir con (1) y emitir según corresponda.

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

Sí, me gusta una solución como esa. Advertencia: .Slice() (un método en TimeSeries ) ahora tiene que devolver un TimeSeriesDouble, por lo que debe ser virtual o usar otros trucos. –

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

¡Perfecto! Esto es justo lo que estaba pensando. –

+0

Ese es el patrón que uso también. –

+0

¿Cuáles son los beneficios de utilizar este enfoque en lugar de este publicado por Jon Skeet? Es casi lo mismo, ¿no? – SOReader

Cuestiones relacionadas