Шаблон для специализации родового класса в С#?

В С# мне иногда хотелось бы сделать специальные методы для определенных "экземпляров" общих классов.

ОБНОВЛЕНИЕ: Следующий код - просто глупый пример более абстрактной проблемы - не слишком много внимания уделяйте временным рядам, а только принципам добавления дополнительных методов для определенного T.

Пример:

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

В случае, когда T двойное, мне нужны некоторые дополнительные методы, такие как Integrate(), Interpolate() и т.д., которые имеют смысл только для double, потому что мне нужно сделать арифметику на них.

Есть несколько способов сделать это, но я не могу найти тот, которым я доволен.

1. Наследовать в специальный класс

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

минус TimeseriesDouble.Slice() вернет новый объект Timeseries<double>, в котором отсутствуют мои специальные методы.

2. Внешние методы

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

минусы: Перерывы с принципами OO. И я не хочу убирать свои методы. Кроме того, этим методам может потребоваться конфиденциальное/защищенное состояние.

3. Методы расширения

То же, что и 2, только с более сильным синтаксисом вызова.

4. Общий базовый класс

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

cons: слишком много дублирования вещей из TimeSeries_base в два подкласса. Базовый класс может стать просто владельцем места для служебных функций для подклассов.

pro: Теперь я могу динамически делать такие вещи, как List<TimeSeries_base>.

5. Просто забудьте об общем классе

I.e., сохранить Timeseries<T> и TimeseriesDouble в коде.

минус. Тогда я не получаю все возможное, чтобы лечить TimeseriesDouble как a Timeseries<T>, например. объединяя два раза с ZIP (A, B), где один из двух пар.


Любые другие идеи? В настоящее время, мне кажется, мне нравится дизайн (1).

Ответ 1

Вы всегда можете использовать трюк с самореферентными дженериками:

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

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

Он может немного похудеть, но он может работать.

Ответ 2

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() { ... }
 }

Ответ 3

Я бы пошел с (1) и отбросил, если нужно.

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

Ответ 4

Я бы подумал о внедрении factory для возврата соответствующего подкласса.