2017-01-14 2 views
2

Я получил следующий абстрактный класс:абстрактных классов реализации ссылочного класса

public abstract class Period 
{ 
    public int Id { get; set; } 
    protected abstract DateTimeOffset StartDate { get; set; } 
    protected abstract DateTimeOffset EndDate { get; set; } 
} 

С следующей реализацией:

public class MonthlyPeriod : Period 
{ 
    private DateTimeOffset startDate { get; set; } 

    public MonthlyPeriod(DateTimeOffset startDate) 
    { 
     this.startDate = startDate; 
    } 

    protected override DateTimeOffset EndDate 
    { 
     get 
     { 
      return startDate.AddMonths(1).AddTicks(-1); 
     } 

     set 
     { 
      startDate = new DateTime(value.Year, value.Month, 1); 
     } 
    } 

    protected override DateTimeOffset StartDate 
    { 
     get 
     { 
      return startDate; 
     } 

     set 
     { 
      startDate = new DateTimeOffset(value.Year, value.Month, 1, 0, 0, 0, value.Offset); 
     } 
    } 
} 

То, что я хотел бы сделать, это включает в себя следующие абстрактные методы на абстрактный класс:

public abstract Period GetPreviousPeriod(); 
public abstract Period GetNextPeriod(); 

И затем в реализации имейте их возвращение MonthlyPeriod а именно:

public override MonthlyPeriod GetNextPeriod() => new MonthlyPeriod(EndDate.AddDays(1)); 
public override MonthlyPeriod GetPreviousPeriod() => new MonthlyPeriod(StartDate.AddDays(-1)); 

Это, однако, имеет очевидный недостаток, который я указать тип метода в абстрактном классе как Period, не MonthlyPeriod.

Есть ли прямой способ определить этот тип отношений?


Лучшее решение у меня до сих пор это:

  1. переключатель абстрактные методы от public к protected.
  2. Сделать реализованные абстрактные методы производят Period, при необходимости.
  3. В абстрактном классе определяют следующие методы:

    public T GetPreviousPeriod<T>() where T : Period 
    { 
        T prevPeriod = GetPreviousPeriod() as T; 
        if (prevPeriod == null) 
        { 
         throw new ArgumentException(); 
        } 
    
        return prevPeriod; 
    } 
    
    public T GetNextPeriod<T>() where T : Period 
    { 
        T nextPeriod = GetNextPeriod() as T; 
        if (nextPeriod == null) 
        { 
         throw new ArgumentException(); 
        } 
    
        return nextPeriod; 
    } 
    

Это работает; однако это не обязательно полное решение. В частности, не хватает времени компиляции, чтобы тип был правильным.

+0

его в природе дженериков, вы не сможете получить ту компиляцию, которую я боюсь.Представьте себе, что я использую вашу сборку, где 'GetNextPeriod ' уже скомпилирован, и я поставляю свой собственный класс, производный от Period .. –

+0

Да, это в значительной степени то, чего я пытаюсь избежать :(Я думал о маркировке Period как 'internal' , но это примерно так же хорошо, как я думаю, я мог бы получить, а затем это вроде потеряет ценность как абстрактный класс. –

ответ

1

Пришло решение, которое работало в контексте моего первоначального вопроса.

  1. Изменение Period в Period<T> where T : Period<T>
  2. Изменение public abstract Period GetPreviousPeriod(); в public abstract T GetPreviousPeriod();
  3. Repeat 2 для GetNextPeriod().
  4. MonthlyPeriod : Period - MonthlyPeriod : Period<MonthlyPeriod>.
-1

Вы можете определить интерфейс, такой как IPeriod, и иметь это как возвращаемый тип в методах интерфейса. Затем ваши конкретные реализации вы можете вернуть MonthlyPeriod, который бы реализовал интерфейс IPeriod

+0

Что было бы в реальном интерфейсе? Я думал, что было бы не создавать такие интерфейсы маркеров, как это. конечно, почему вы сошли с голодания ... это допустимое обходное решение. –

2

Вместо того чтобы принудительно использовать этот метод в базовом классе, что делать, если вы создаете некоторые методы расширения, которые создают эти новые экземпляры и возвращают их?

Также не выглядит правильным, что период также работает как «PeriodFactory», поскольку он нарушает S в SOLID.

public static MonthlyPeriod GetNextPeriod(this MonthlyPeriod period) 
{ 
    return new MonthlyPeriod(period.EndDate); 
} 
+0

Глядя на него снова, вы правы. Абстрактный шаблон фабрики - это абсолютно правильный способ справиться с этим. Методы расширения по-прежнему меня не доставят, поскольку У меня не было бы обещания, что будущие реализации сделают правильный метод расширения. Например, если FiscalYearPeriod «FY17» стартовал 1 октября 2016 года, тогда я был бы в ужасном мире. –

+0

So Я не принял ответа здесь. Это не обязательно был «правильный» ответ на этот вопрос и более отвлекался на другой подход к проблеме. Я смог напрямую ответить на конкретный вопрос (см. Мой ответ). –

+0

Все в порядке, ваш ответ абсолютно правильный. Вы также должны отметить это как правильный ответ на этот вопрос. –

Смежные вопросы