Шаблон для специализации родового класса в С#?
В С# мне иногда хотелось бы сделать специальные методы для определенных "экземпляров" общих классов.
ОБНОВЛЕНИЕ: Следующий код - просто глупый пример более абстрактной проблемы - не слишком много внимания уделяйте временным рядам, а только принципам добавления дополнительных методов для определенного 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 для возврата соответствующего подкласса.