Есть два практических варианта, в зависимости от того, насколько строгими должны быть ваши кодовые контракты.
Вы можете переместить логику в виртуальный метод и позволить дочерним типам перегружать поведение, если они того пожелают.
internal abstract class Base
{
...
public virtual void Invoke(string message)
{
Time = DateTime.Now;
}
}
internal class SubA : Base
{
public override void Invoke(string message)
{
base.Invoke(message);
// Do A
}
}
internal class SubB : Base
{
public override void Invoke(string message)
{
base.Invoke(message);
// Do B
}
}
Однако это позволяет производным типам вообще не вызывать базовый метод.
Если это катастрофически, если базовая функциональность не вызывается, и вы хотите больше уверенности в ожидаемом поведении, вы можете заключить более сильный контракт, предоставив точку внедрения в середину базового метода:
internal abstract class Base
{
...
public void Invoke(string message)
{
Time = DateTime.Now;
this.InvokeCore(message);
}
protected abstract void InvokeCore(string message);
}
internal class SubA : Base
{
public override void Invoke(string message)
{
// Do A
}
}
internal class SubB : Base
{
public override void InvokeCore(string message)
{
// Do B
}
}