Reputation: 110
I've got the following abstract class:
public abstract class Period
{
public int Id { get; set; }
protected abstract DateTimeOffset StartDate { get; set; }
protected abstract DateTimeOffset EndDate { get; set; }
}
With the following implementation:
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);
}
}
}
What I'd like to do is include the following abstract methods on the abstract class:
public abstract Period GetPreviousPeriod();
public abstract Period GetNextPeriod();
And then in the implementation have them return MonthlyPeriod
specifically:
public override MonthlyPeriod GetNextPeriod() => new MonthlyPeriod(EndDate.AddDays(1));
public override MonthlyPeriod GetPreviousPeriod() => new MonthlyPeriod(StartDate.AddDays(-1));
This however has the obvious flaw that I specify the type of the method in the abstract class as being Period
, not MonthlyPeriod
.
Is there a straight forward way to define this type of relationship?
The best solution I've got so far is this:
public
to protected
.Period
, as required.In the abstract class define the following methods:
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;
}
This works; however, it isn't necessarily a complete solution. In particular it's missing the compile time promise that the type is correct.
Upvotes: 2
Views: 352
Reputation: 110
Came up with a solution that worked in context of my original question.
Period
to Period<T> where T : Period<T>
public abstract Period GetPreviousPeriod();
to public abstract T GetPreviousPeriod();
GetNextPeriod()
.MonthlyPeriod : Period
to MonthlyPeriod : Period<MonthlyPeriod>
.Upvotes: 1
Reputation: 644
Instead of forcing that method on the base class, what if you create some extension methods, which create those new instances and returns them?
Also, it doesn't look right that a Period also works as a "PeriodFactory", as it violates the S in SOLID.
public static MonthlyPeriod GetNextPeriod(this MonthlyPeriod period)
{
return new MonthlyPeriod(period.EndDate);
}
Upvotes: 2
Reputation: 35
You could define an interface like IPeriod and have that as the return type in the interface methods. Then your concrete implementations you can return MonthlyPeriod which would have implemented the interface IPeriod
Upvotes: -1