views:

1817

answers:

4

In C# I sometimes wish I could make special methods for certain "instantiations" of generic classes.

UPDATE: The following code is just a dumb example of a more abstract problem - don't focus too much on time series, just the principles of "adding extra methods" for certain T.

Example:

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

In the case where T is double, I would like some extra methods, like Integrate(), Interpolate() and so on that make only sense for double, because I need to do arithmetic on them.

There are several ways to do this, but I cannot find one that I'm satisfied with.

1. Inherit into a special class

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

cons: TimeseriesDouble.Slice() will return a new Timeseries<double> object, now missing my special methods.

2. External methods

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

cons: Breaks with OO principles. And I don't want to put my methods away. Also, the methods might need private/protected state.

3. Extension methods

Same as 2, just with a nicer calling syntax.

4. Common base class

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

cons: too much duplication of things from TimeSeries_base into the two subclasses. The base class might become just a place holder for utility functions for the sub classes.

pro: I can now do things like List<TimeSeries_base> dynamically.

5. Just forget about a common class

I.e., keep Timeseries<T> and TimeseriesDouble separate in the code.

cons: Then I don't get all the benefit of treating a TimeseriesDouble like a TimeSeries<T>, e.g. combining two timeseries with ZIP(A,B), where one happens to be of doubles.


Any other ideas? Currently, I think I like the design (1) best.

A: 

I'd consider implementing a factory to return the appropriate subclass.

FlySwat
+1  A: 

I would go with (1) and cast as appropriate.

 TimeSeriesDouble tsD = new TimeSeriesDouble();
 TimeSeriesDouble subTSD = tsD.Slice(...) as TimeSeriesDouble;
tvanfosson
Yes, I like a solution like that. Caveat: .Slice() (a method in TimeSeries<T>) now has to actually return a TimeSeriesDouble, so it must be virtual or use other tricks.
Bjarke Ebert
+4  A: 

You could always use the self-referential generics trick:

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

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

It can get a bit brain-bending, but it can work.

Jon Skeet
In my concrete library I think I'll go for something more straight-forward. Nice trick, though, I'll keep it in my bag :-)
Bjarke Ebert
+5  A: 
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() { ... }
 }
Mark Cidade
Perfect! This is just what I was thinking.
Neil Whitaker
That's the pattern I use as well.
Alexey Romanov