Шаблон для специализации универсального класса в C #? - PullRequest
10 голосов
/ 12 ноября 2008

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

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

Пример:

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

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

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

1. Наследовать в особый класс

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

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

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

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

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

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

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

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

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

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

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

5. Просто забудь про общий класс

Т.е., в коде держите Timeseries<T> и TimeseriesDouble отдельно.

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


Есть еще идеи? В настоящее время я думаю, что мне больше всего нравится дизайн (1).

Ответы [ 4 ]

12 голосов
/ 12 ноября 2008

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

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

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

Это может немного напрячь мозг, но это может сработать.

9 голосов
/ 12 ноября 2008
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() { ... }
 }
1 голос
/ 12 ноября 2008

Я бы пошел с (1) и произвел бы соответственно.

 TimeSeriesDouble tsD = new TimeSeriesDouble();
 TimeSeriesDouble subTSD = tsD.Slice(...) as TimeSeriesDouble;
0 голосов
/ 12 ноября 2008

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

...